Initial import of Cobalt 2.8885 2016-07-27
diff --git a/src/.clang-format b/src/.clang-format
new file mode 100644
index 0000000..d5c7b08
--- /dev/null
+++ b/src/.clang-format
@@ -0,0 +1,3 @@
+# Defines the Chromium style for automatic reformatting.
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+BasedOnStyle: Chromium
diff --git a/src/AUTHORS b/src/AUTHORS
new file mode 100644
index 0000000..5a7ed82
--- /dev/null
+++ b/src/AUTHORS
@@ -0,0 +1,12 @@
+# Names should be added to this file with this pattern:
+#
+# For individuals:
+# Name <email address>
+#
+# For organizations:
+# Organization <fnmatch pattern>
+#
+# See python fnmatch module documentation for more information.
+
+The Chromium Authors <*@chromium.org>
+Google Inc. <*@google.com>
diff --git a/src/LICENSE b/src/LICENSE
new file mode 100644
index 0000000..3d0f7d3
--- /dev/null
+++ b/src/LICENSE
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. 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/README.cobalt.txt b/src/README.cobalt.txt
new file mode 100644
index 0000000..bc2d9c7
--- /dev/null
+++ b/src/README.cobalt.txt
@@ -0,0 +1,2 @@
+This is a fork of the chromium repository at http://git.chromium.org/git/chromium.git
+
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..2d2efba
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,247 @@
+# Cobalt
+
+## Overview
+
+Cobalt is a lightweight application container (i.e. an application runtime, like
+a JVM or the Flash Player) that is compatible with a subset of the W3C HTML5
+specifications. If you author a single-page web application (SPA) that complies
+with the Cobalt Subset of W3C standards, it will run as well as possible on all
+the devices that Cobalt supports.
+
+
+## Motivation
+
+The Cobalt Authors originally maintained a port of Chromium called H5VCC, the
+HTML5 Video Container for Consoles, ported to each of the major game consoles,
+designed to run our HTML5-based video browse and play application. This took a
+long time to port to each platform, consisted of 9 million lines of C++ code
+(before we touched it), was dangerous to modify without unintended consequences,
+and was thoroughly designed for a resource-rich, multi-process environment
+(e.g. a desktop, laptop, or modern smartphone).
+
+After wrestling with this for several years, we imagined an environment that was
+not designed for traditional scrolling web content, but was intended to be a
+runtime environment for rich client applications built with the same
+technologies -- HTML, CSS, JavaScript -- and designed from the ground-up to run
+on constrained, embedded, Living Room Consumer Electronics (CE) devices, such as
+Game Consoles, Set-Top Boxes (e.g. Cable, Satellite), OTT devices (e.g. Roku,
+Apple TV, Chromecast, Fire TV), Blu-ray Disc Players, and Smart TVs.
+
+These constraints (not intended to be a canonical list) make this device
+spectrum vastly different from the desktop computer environment targeted by
+Chromium, FireFox, and IE:
+
+ * **Limited Memory.** All except the very latest, expensive CE devices have a
+ very small amount of memory available for applications. This usually is
+ somewhere in the ballpark of 200MB-500MB, including graphics and media
+ memory, as opposed to multiple gigabytes of CPU memory (and more gigabytes
+ of GPU memory) in modern desktop and laptop computers, and mobile devices.
+ * **Slow CPUs.** Most CE devices have much slower CPUs than what is available
+ on even a budget desktop computer. Minor performance concerns can be greatly
+ exaggerated, which seriously affects priorities.
+ * **Fewer cores.** CE System-on-a-Chip (SoC) processors often do not have as
+ many processor cores as we are used to in modern computers. Many deployed
+ devices still only have a single core.
+ * **Sometimes No GPU.** Not all CE devices have a monster GPU to throw shaders
+ at to offload CPU work. A different strategy is required to maximize
+ leverage of an accelerated blitter, which is all some older devices
+ have. Some newer CE devices have a GPU, but it's not nearly as powerful as
+ what one would even see on a laptop.
+ * **Sometimes No JIT.** Many CE devices are dealing with "High-Value Content,"
+ and, as such, are very sensitive to security concerns. Ensuring that
+ writable pages are not executable is a strong security protocol that can
+ prevent a wide spectrum of attacks. But, as a side effect, this also means
+ no ability to JIT.
+ * **Heterogenous Development Environments.** This is slowly evening out, but
+ all CE devices run on custom hardware, often with proprietary methods of
+ building, packaging, deploying, and running programs. Almost all CE devices
+ have ARM or MIPS processors instead of the more familiar x86. Sometimes the
+ toolchain doesn't support contemporary C++11 features. Sometimes the OS
+ isn't POSIX, or it tries to be, but it is only partially implemented.
+ Sometimes the program entry point is in another language or architecture
+ that requires a "trampoline" over to native binary code.
+ * **No navigation.** The point of a Single-Page Application is that you don't
+ go through the HTTP page dance every time you switch screens. It's slow, and
+ provides poor user feedback, not to mention a jarring transition. Instead,
+ one loads data from an XMLHttpRequest (XHR), and then updates one's DOM to
+ reflect the new data. AJAX! Web 2.0!!
+ * **No scrolling.** Well, full-screen, 10-foot UI SPA apps might scroll, but
+ not like traditional web pages, with scroll bars and a mouse
+ wheel. Scrolling is generally built into the app very carefully, with
+ support for a Directional Pad and a focus cursor.
+
+
+## Architecture
+
+The Cobalt Authors forked H5VCC, removed most of the Chromium code -- in
+particular WebCore and the Chrome Renderer and Compositor -- and built up from
+scratch an implementation of a simplified subset of HTML, the CSS Box Model for
+layout, and the Web APIs that were really needed to build a full-screen SPA
+browse and play application.
+
+The Cobalt technology stack has these major components, roughly in a high-level
+application to a low-level platform order:
+
+ * **Web Implementation** - This is where the W3C standards are implemented,
+ ultimately producing an annotated DOM tree that can be passed into the
+ Layout Engine to produce a Render Tree.
+ * **JavaScript Engine** - We have, perhaps surprisingly, *not* written our own
+ JavaScript Engine from scratch. Because of the JITing constraint, we have to
+ be flexible with which engine(s) we work with. We have a bindings layer that
+ interfaces with the JS Engine so that application script can interface with
+ natively-backed objects (like DOM elements).
+ * **Layout Engine** - The Layout Engine takes an annotated DOM Document
+ produced by the Web Implementation and JavaScript Engine working together,
+ and calculates a tree of rendering commands to send to the renderer (i.e. a
+ Render Tree). It caches intermediate layout artifacts so that subsequent
+ incremental layouts can be sped up.
+ * **Renderer/Skia** - The Renderer walks a Render Tree produced by the Layout
+ Engine, rasterizes it using the third-party graphics library Skia, and swaps
+ it to the front buffer. There are two major paths here, one using Hardware
+ Skia on OpenGL ES 2.0, and one using Software Skia combined with the
+ hardware-accelerated Starboard Blitter. Note that the renderer runs in a
+ different thread from the Layout Engine, and can interpolate animations that
+ do not require re-layout. This decouples rendering from Layout and
+ JavaScript, allowing for smooth, consistent animations on platforms with a
+ variety of capabilities.
+ * **Net / Media** - These are Chromium's Network and Media engines. We are
+ using them directly, as they don't cause any particular problems with the
+ extra constraints listed above.
+ * **Base** - This is Chromium's "Base" library, which contains a wide variety
+ of useful things used throughout Cobalt, Net, and Media. Cobalt uses a
+ combination of standard C++ containers (e.g. vector, string) and Base as the
+ foundation library for all of its code.
+ * **Other Third-party Libraries** - Most of these are venerable, straight-C,
+ open-source libraries that are commonly included in other open-source
+ software. Mostly format decoders and parsers (e.g. libpng, libxml2,
+ zlib). We fork these from Chromium, as we want them to be the most
+ battle-tested versions of these libraries.
+ * **Starboard/Glimp/ANGLE** - **Starboard** is the Cobalt porting
+ interface. One major difference between Cobalt and Chromium is that we have
+ created a hard straight-C porting layer, and ported ALL of the compiled
+ code, including Base and all third-party libraries, to use it instead of
+ directly using POSIX standard libraries, which are not consistent, even on
+ modern systems (see Android, Windows, MacOS X, and iOS). Additionally,
+ Starboard includes APIs that haven't been effectively standardized across
+ platforms, such as display Window creation, Input events, and Media
+ playback. **Glimp** is an OpenGL ES 2.0 implementation framework, built by
+ the Cobalt team directly on Starboard, designed to adapt proprietary 3D APIs
+ to GLES2. **ANGLE** Is a third-party library that adapts DirectX to GLES2,
+ similar to Glimp, but only for DirectX.
+
+Cobalt is like a flaky layered pastry - perhaps Baklava. It shouldn't be too
+difficult to rip the Web Implementation and Layout off the top, and just use the
+Renderer, or even to just use Base + Starboard + GLES2 as the basis of a new
+project.
+
+
+## The Cobalt Subset
+
+> Oh, we got both kinds of HTML tags,\
+> we got `<span>` and `<div>`!
+
+See the [Cobalt Subset specification](TODO) for more details on which tags,
+properties, and Web APIs are supported in Cobalt.
+
+*More to come.*
+
+
+## Interesting Source Locations
+
+All source locations are specified relative to `src/` (this directory).
+
+ * `base/` - Chromium's Base library. Contains common utilities, and a light
+ platform abstraction, which has been superceded in Cobalt by Starboard.
+ * `net/` - Chromium's Network library. Contains enough infrastructure to
+ support the network needs of an HTTP User-Agent (like Chromium or Cobalt),
+ an HTTP server, a DIAL server, and several abstractions for networking
+ primitives. Also contains SPDY and QUIC implementations.
+ * `media/` - Chromium's Media library. Contains all the code that parses,
+ processes, and manages buffers of video and audio data. Media decoding is
+ passed off to decoding hardware, wherever possible.
+ * `cobalt/` - The home of all Cobalt application code. This includes the Web
+ Implementation, Layout Engine, Renderer, and some other Cobalt-specific
+ features.
+ * `cobalt/build/` - The core build generation system, `gyp_cobalt`, and
+ configurations for supported platforms. (NOTE: This should eventually be
+ mostly moved into `starboard/`.)
+ * `starboard/` - Cobalt's porting layer. Please see Starboard's
+ [`README.md`](starboard/README.md) for more detailed information about
+ porting Starboard (and Cobalt) to a new platform.
+ * `third_party/` - Where all of Cobalt's third-party dependencies live. We
+ don't mean to be perjorative, we love our third-party libraries! This
+ location is dictated by Google OSS release management rules...
+ * `third_party/starboard/` - The location for third-party ports. This
+ directory will be scanned automatically by gyp_cobalt for available
+ Starboard ports.
+
+
+## Building and Running the Code
+
+Here's a quick and dirty guide to get to build the code on Linux.
+
+ 1. Install the provided `depot_tools` archive into your favorite directory. It
+ has been slightly modified from Chromium's `depot_tools`.
+ 2. Add that directory to the end of your `$PATH`.
+ 3. Ensure you have these packages installed: `sudo apt-get install
+ libgles2-mesa-dev libpulse-dev libavformat-dev libavresample-dev
+ libasound2-dev libxrender-dev libxcomposite-dev`
+ 4. Ensure you have the standard C++ header files installed
+ (e.g. `libstdc++-4.8-dev`).
+ 5. Remove bison-3 and install bison-2.7, or just make sure that bison-2.7 is
+ before bison-3 on your `$PATH`. (NOTE: We plan on moving to bison-3 in the
+ future.)
+
+ $ sudo apt-get remove bison
+ $ sudo apt-get install m4
+ $ wget http://ftp.gnu.org/gnu/bison/bison-2.7.1.tar.gz
+ $ tar zxf bison-2.7.1.tar.gz
+ $ cd bison-2.7.1
+ $ sh configure && make && sudo make install
+ $ which bison
+ /usr/local/bin/bison
+ $ bison --version
+ bison (GNU Bison) 2.7.12-4996
+
+ 6. (From this directory) run GYP:
+
+ cobalt/build/gyp_cobalt -C debug linux-x64x11
+
+ 7. If you get a "clang not found" error, add the path to Cobalt's clang to
+ your `$PATH` and rerun `gyp_cobalt` as above. For example:
+ `/path/to/cobalt/src/third_party/llvm-build/Release+Asserts/bin`
+ 8. Run Ninja:
+
+ ninja -C out/linux-x64x11_debug cobalt
+
+ 9. Run Cobalt:
+
+ out/linux-x64x11_debug/cobalt [--url=<url>]
+
+ * If you want to use `http` instead of `https`, you must pass the
+ `--allow_http` flag to the Cobalt command-line.
+ * If you want to connect to an `https` host that doesn't have a
+ globally-validatable certificate, you must pass the
+ `--ignore_certificate_errors` flag to the Cobalt command-line.
+ * See `cobalt/browser/switches.cc` for more command-line options.
+
+
+## Build Types
+
+Cobalt has four build optimization levels, going from the slowest, least
+optimized, with the most debug information at the top (debug) to the fastest,
+most optimized, and with the least debug information at the bottom (gold):
+
+ | Type | Optimizations | Logging | Asserts | Debug Info | Console |
+ | :---- | :------------ | :------ | :------ | :--------- | :------- |
+ | debug | None | Full | Full | Full | Enabled |
+ | devel | Full | Full | Full | Full | Enabled |
+ | qa | Full | Limited | None | None | Enabled |
+ | gold | Full | None | None | None | Disabled |
+
+When building for release, you should always use a gold build for the final
+product.
+
+ $ cobalt/build/gyp_cobalt -C gold linux-x64x11
+ $ ninja -C out/linux-x64x11_gold cobalt
+ $ out/linux-x64x11_gold/cobalt
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(F