summaryrefslogtreecommitdiffstats
path: root/chromium/base
diff options
context:
space:
mode:
authorJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-08 14:30:41 +0200
committerJocelyn Turcotte <jocelyn.turcotte@digia.com>2014-08-12 13:49:54 +0200
commitab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch)
tree498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/base
parent4ce69f7403811819800e7c5ae1318b2647e778d1 (diff)
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/base')
-rw-r--r--chromium/base/BUILD.gn1373
-rw-r--r--chromium/base/DEPS7
-rw-r--r--chromium/base/PRESUBMIT.py14
-rw-r--r--chromium/base/allocator/BUILD.gn248
-rw-r--r--chromium/base/allocator/allocator.gyp34
-rw-r--r--chromium/base/allocator/allocator_shim.cc63
-rw-r--r--chromium/base/allocator/allocator_unittest.cc (renamed from chromium/base/allocator/allocator_unittests.cc)11
-rwxr-xr-xchromium/base/allocator/prep_libc.py2
-rw-r--r--chromium/base/allocator/type_profiler_map_unittest.cc (renamed from chromium/base/allocator/type_profiler_map_unittests.cc)2
-rw-r--r--chromium/base/allocator/type_profiler_unittest.cc (renamed from chromium/base/allocator/type_profiler_unittests.cc)2
-rw-r--r--chromium/base/android/OWNERS2
-rw-r--r--chromium/base/android/activity_state_list.h16
-rw-r--r--chromium/base/android/activity_status.cc66
-rw-r--r--chromium/base/android/activity_status.h98
-rw-r--r--chromium/base/android/application_state_list.h17
-rw-r--r--chromium/base/android/application_status_listener.cc74
-rw-r--r--chromium/base/android/application_status_listener.h79
-rw-r--r--chromium/base/android/application_status_listener_unittest.cc (renamed from chromium/base/android/activity_status_unittest.cc)56
-rw-r--r--chromium/base/android/build_info.cc3
-rw-r--r--chromium/base/android/build_info.h5
-rw-r--r--chromium/base/android/content_uri_utils.cc7
-rw-r--r--chromium/base/android/content_uri_utils.h3
-rw-r--r--chromium/base/android/context_types.cc26
-rw-r--r--chromium/base/android/context_types.h22
-rw-r--r--chromium/base/android/event_log.cc20
-rw-r--r--chromium/base/android/event_log.h22
-rw-r--r--chromium/base/android/jni_generator/jni_generator.gyp3
-rw-r--r--chromium/base/android/library_loader/library_loader_hooks.cc73
-rw-r--r--chromium/base/android/library_loader/library_loader_hooks.h51
-rw-r--r--chromium/base/android/linker/DEPS4
-rw-r--r--chromium/base/android/path_utils_unittest.cc3
-rw-r--r--chromium/base/android/scoped_java_ref.cc18
-rw-r--r--chromium/base/android/scoped_java_ref.h17
-rw-r--r--chromium/base/android/sys_utils.h2
-rw-r--r--chromium/base/android/trace_event_binding.cc169
-rw-r--r--chromium/base/android/trace_event_binding.h18
-rw-r--r--chromium/base/atomicops.h11
-rw-r--r--chromium/base/atomicops_internals_arm64_gcc.h307
-rw-r--r--chromium/base/atomicops_internals_arm_gcc.h15
-rw-r--r--chromium/base/atomicops_internals_mac.h42
-rw-r--r--chromium/base/atomicops_internals_tsan.h294
-rw-r--r--chromium/base/atomicops_internals_x86_gcc.cc38
-rw-r--r--chromium/base/atomicops_internals_x86_gcc.h27
-rw-r--r--chromium/base/atomicops_internals_x86_msvc.h10
-rw-r--r--chromium/base/atomicops_unittest.cc10
-rw-r--r--chromium/base/base.gyp511
-rw-r--r--chromium/base/base.gypi224
-rw-r--r--chromium/base/base.isolate29
-rw-r--r--chromium/base/base_nacl.gyp71
-rw-r--r--chromium/base/base_paths.cc35
-rw-r--r--chromium/base/base_paths.h4
-rw-r--r--chromium/base/base_paths_android.cc3
-rw-r--r--chromium/base/base_paths_mac.mm3
-rw-r--r--chromium/base/base_paths_posix.cc3
-rw-r--r--chromium/base/base_paths_posix.h2
-rw-r--r--chromium/base/base_paths_win.cc13
-rw-r--r--chromium/base/base_paths_win.h2
-rw-r--r--chromium/base/base_switches.cc9
-rw-r--r--chromium/base/base_switches.h2
-rw-r--r--chromium/base/base_unittests.isolate8
-rw-r--r--chromium/base/base_untrusted.gyp41
-rw-r--r--chromium/base/basictypes.h373
-rw-r--r--chromium/base/big_endian.cc97
-rw-r--r--chromium/base/big_endian.h102
-rw-r--r--chromium/base/big_endian_unittest.cc100
-rw-r--r--chromium/base/bind.h6
-rw-r--r--chromium/base/bind.h.pump4
-rw-r--r--chromium/base/bind_helpers.h2
-rw-r--r--chromium/base/bind_unittest.cc4
-rw-r--r--chromium/base/bind_unittest.nc3
-rw-r--r--chromium/base/callback_list.h30
-rw-r--r--chromium/base/callback_list.h.pump30
-rw-r--r--chromium/base/cancelable_callback_unittest.cc2
-rw-r--r--chromium/base/command_line.cc14
-rw-r--r--chromium/base/command_line.h19
-rw-r--r--chromium/base/command_line_unittest.cc12
-rw-r--r--chromium/base/compiler_specific.h34
-rw-r--r--chromium/base/containers/hash_tables.h4
-rw-r--r--chromium/base/containers/scoped_ptr_hash_map.h42
-rw-r--r--chromium/base/containers/small_map_unittest.cc25
-rw-r--r--chromium/base/containers/stack_container.h7
-rw-r--r--chromium/base/cpu.cc70
-rw-r--r--chromium/base/critical_closure.h53
-rw-r--r--chromium/base/critical_closure_internal_ios.mm17
-rw-r--r--chromium/base/critical_closure_ios.mm49
-rw-r--r--chromium/base/debug/OWNERS2
-rw-r--r--chromium/base/debug/asan_invalid_access.cc94
-rw-r--r--chromium/base/debug/asan_invalid_access.h47
-rw-r--r--chromium/base/debug/debug_on_start_win.cc74
-rw-r--r--chromium/base/debug/debug_on_start_win.h83
-rw-r--r--chromium/base/debug/debugger.h4
-rw-r--r--chromium/base/debug/debugger_posix.cc22
-rw-r--r--chromium/base/debug/debugger_win.cc89
-rw-r--r--chromium/base/debug/dump_without_crashing.cc32
-rw-r--r--chromium/base/debug/dump_without_crashing.h30
-rw-r--r--chromium/base/debug/gdi_debug_util_win.cc129
-rw-r--r--chromium/base/debug/gdi_debug_util_win.h23
-rw-r--r--chromium/base/debug/leak_tracker.h6
-rw-r--r--chromium/base/debug/proc_maps_linux.cc23
-rw-r--r--chromium/base/debug/proc_maps_linux_unittest.cc32
-rw-r--r--chromium/base/debug/sanitizer_options.cc117
-rw-r--r--chromium/base/debug/stack_trace.cc2
-rw-r--r--chromium/base/debug/stack_trace.h13
-rw-r--r--chromium/base/debug/stack_trace_android.cc10
-rw-r--r--chromium/base/debug/stack_trace_ios.mm53
-rw-r--r--chromium/base/debug/stack_trace_posix.cc317
-rw-r--r--chromium/base/debug/stack_trace_unittest.cc4
-rw-r--r--chromium/base/debug/stack_trace_win.cc19
-rw-r--r--chromium/base/debug/trace_event.h51
-rw-r--r--chromium/base/debug/trace_event_android.cc1
-rw-r--r--chromium/base/debug/trace_event_impl.cc254
-rw-r--r--chromium/base/debug/trace_event_impl.h59
-rw-r--r--chromium/base/debug/trace_event_memory.cc4
-rw-r--r--chromium/base/debug/trace_event_memory_unittest.cc4
-rw-r--r--chromium/base/debug/trace_event_synthetic_delay.cc233
-rw-r--r--chromium/base/debug/trace_event_synthetic_delay.h166
-rw-r--r--chromium/base/debug/trace_event_synthetic_delay_unittest.cc156
-rw-r--r--chromium/base/debug/trace_event_unittest.cc337
-rw-r--r--chromium/base/debug/tsan_suppressions.cc297
-rw-r--r--chromium/base/file_descriptor_posix.h17
-rw-r--r--chromium/base/file_util.cc64
-rw-r--r--chromium/base/file_util.h160
-rw-r--r--chromium/base/file_util_linux.cc54
-rw-r--r--chromium/base/file_util_mac.mm19
-rw-r--r--chromium/base/file_util_posix.cc215
-rw-r--r--chromium/base/file_util_unittest.cc464
-rw-r--r--chromium/base/file_util_win.cc273
-rw-r--r--chromium/base/file_version_info_mac.h34
-rw-r--r--chromium/base/file_version_info_mac.mm50
-rw-r--r--chromium/base/file_version_info_win.cc30
-rw-r--r--chromium/base/file_version_info_win.h32
-rw-r--r--chromium/base/files/OWNERS5
-rw-r--r--chromium/base/files/file.cc86
-rw-r--r--chromium/base/files/file.h51
-rw-r--r--chromium/base/files/file_path.cc20
-rw-r--r--chromium/base/files/file_path.h17
-rw-r--r--chromium/base/files/file_path_watcher.cc17
-rw-r--r--chromium/base/files/file_path_watcher.h5
-rw-r--r--chromium/base/files/file_path_watcher_browsertest.cc67
-rw-r--r--chromium/base/files/file_path_watcher_fsevents.cc263
-rw-r--r--chromium/base/files/file_path_watcher_fsevents.h73
-rw-r--r--chromium/base/files/file_path_watcher_kqueue.cc173
-rw-r--r--chromium/base/files/file_path_watcher_kqueue.h132
-rw-r--r--chromium/base/files/file_path_watcher_linux.cc450
-rw-r--r--chromium/base/files/file_path_watcher_mac.cc60
-rw-r--r--chromium/base/files/file_path_watcher_win.cc31
-rw-r--r--chromium/base/files/file_posix.cc190
-rw-r--r--chromium/base/files/file_proxy.cc359
-rw-r--r--chromium/base/files/file_proxy.h142
-rw-r--r--chromium/base/files/file_proxy_unittest.cc370
-rw-r--r--chromium/base/files/file_unittest.cc139
-rw-r--r--chromium/base/files/file_util_proxy.cc331
-rw-r--r--chromium/base/files/file_util_proxy.h143
-rw-r--r--chromium/base/files/file_util_proxy_unittest.cc310
-rw-r--r--chromium/base/files/file_win.cc60
-rw-r--r--chromium/base/files/important_file_writer.cc83
-rw-r--r--chromium/base/files/important_file_writer.h18
-rw-r--r--chromium/base/files/important_file_writer_unittest.cc73
-rw-r--r--chromium/base/files/memory_mapped_file.cc27
-rw-r--r--chromium/base/files/memory_mapped_file.h30
-rw-r--r--chromium/base/files/memory_mapped_file_posix.cc19
-rw-r--r--chromium/base/files/memory_mapped_file_win.cc71
-rw-r--r--chromium/base/files/scoped_file.cc35
-rw-r--r--chromium/base/files/scoped_file.h61
-rw-r--r--chromium/base/files/scoped_temp_dir_unittest.cc16
-rw-r--r--chromium/base/format_macros.h28
-rw-r--r--chromium/base/hash.cc73
-rw-r--r--chromium/base/hash.h25
-rw-r--r--chromium/base/hash_unittest.cc82
-rw-r--r--chromium/base/i18n/break_iterator.cc64
-rw-r--r--chromium/base/i18n/break_iterator.h19
-rw-r--r--chromium/base/i18n/build_utf8_validator_tables.cc466
-rw-r--r--chromium/base/i18n/file_util_icu.cc12
-rw-r--r--chromium/base/i18n/file_util_icu_unittest.cc4
-rw-r--r--chromium/base/i18n/icu_util.cc80
-rw-r--r--chromium/base/i18n/icu_util.h10
-rw-r--r--chromium/base/i18n/rtl.cc39
-rw-r--r--chromium/base/i18n/rtl.h4
-rw-r--r--chromium/base/i18n/rtl_unittest.cc72
-rw-r--r--chromium/base/i18n/streaming_utf8_validator.cc59
-rw-r--r--chromium/base/i18n/streaming_utf8_validator.h63
-rw-r--r--chromium/base/i18n/streaming_utf8_validator_perftest.cc234
-rw-r--r--chromium/base/i18n/streaming_utf8_validator_unittest.cc412
-rw-r--r--chromium/base/i18n/utf8_validator_tables.cc55
-rw-r--r--chromium/base/i18n/utf8_validator_tables.h29
-rw-r--r--chromium/base/id_map.h4
-rw-r--r--chromium/base/id_map_unittest.cc54
-rw-r--r--chromium/base/ios/device_util.h23
-rw-r--r--chromium/base/ios/device_util.mm33
-rw-r--r--chromium/base/ios/device_util_unittest.mm34
-rw-r--r--chromium/base/ios/ios_util.h3
-rw-r--r--chromium/base/ios/ios_util.mm4
-rw-r--r--chromium/base/json/json_file_value_serializer.cc5
-rw-r--r--chromium/base/json/json_string_value_serializer.cc3
-rw-r--r--chromium/base/json/json_value_serializer_unittest.cc7
-rw-r--r--chromium/base/json/json_writer.cc300
-rw-r--r--chromium/base/json/json_writer.h26
-rw-r--r--chromium/base/json/json_writer_unittest.cc137
-rw-r--r--chromium/base/lazy_instance.h4
-rw-r--r--chromium/base/linux_util.cc125
-rw-r--r--chromium/base/linux_util.h9
-rw-r--r--chromium/base/location.cc6
-rw-r--r--chromium/base/logging.cc167
-rw-r--r--chromium/base/logging.h206
-rw-r--r--chromium/base/logging_unittest.cc80
-rw-r--r--chromium/base/logging_win.cc1
-rw-r--r--chromium/base/mac/bind_objc_block.h19
-rw-r--r--chromium/base/mac/bind_objc_block_unittest.mm13
-rw-r--r--chromium/base/mac/cocoa_protocols.h11
-rw-r--r--chromium/base/mac/foundation_util_unittest.mm73
-rw-r--r--chromium/base/mac/launch_services_util.h2
-rw-r--r--chromium/base/mac/mac_logging.h18
-rw-r--r--chromium/base/mac/mac_util.h28
-rw-r--r--chromium/base/mac/mac_util.mm56
-rw-r--r--chromium/base/mac/mac_util_unittest.mm38
-rw-r--r--chromium/base/mac/mach_logging.cc87
-rw-r--r--chromium/base/mac/mach_logging.h167
-rw-r--r--chromium/base/mac/os_crash_dumps.cc9
-rw-r--r--chromium/base/mac/scoped_cftyperef.h85
-rw-r--r--chromium/base/mac/scoped_mach_port.cc25
-rw-r--r--chromium/base/mac/scoped_mach_port.h55
-rw-r--r--chromium/base/mac/scoped_mach_vm.cc33
-rw-r--r--chromium/base/mac/scoped_mach_vm.h93
-rw-r--r--chromium/base/mac/scoped_typeref.h132
-rw-r--r--chromium/base/mac/sdk_forward_declarations.h194
-rw-r--r--chromium/base/mac/sdk_forward_declarations.mm14
-rw-r--r--chromium/base/macros.h313
-rw-r--r--chromium/base/md5.cc16
-rw-r--r--chromium/base/md5.h6
-rw-r--r--chromium/base/md5_unittest.cc45
-rw-r--r--chromium/base/memory/aligned_memory.h10
-rw-r--r--chromium/base/memory/aligned_memory_unittest.cc9
-rw-r--r--chromium/base/memory/discardable_memory.cc86
-rw-r--r--chromium/base/memory/discardable_memory.h78
-rw-r--r--chromium/base/memory/discardable_memory_allocator_android.h65
-rw-r--r--chromium/base/memory/discardable_memory_allocator_android_unittest.cc232
-rw-r--r--chromium/base/memory/discardable_memory_android.cc299
-rw-r--r--chromium/base/memory/discardable_memory_android.h37
-rw-r--r--chromium/base/memory/discardable_memory_ashmem.cc75
-rw-r--r--chromium/base/memory/discardable_memory_ashmem.h55
-rw-r--r--chromium/base/memory/discardable_memory_ashmem_allocator.cc (renamed from chromium/base/memory/discardable_memory_allocator_android.cc)303
-rw-r--r--chromium/base/memory/discardable_memory_ashmem_allocator.h93
-rw-r--r--chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc319
-rw-r--r--chromium/base/memory/discardable_memory_emulated.cc79
-rw-r--r--chromium/base/memory/discardable_memory_emulated.h22
-rw-r--r--chromium/base/memory/discardable_memory_linux.cc62
-rw-r--r--chromium/base/memory/discardable_memory_mac.cc226
-rw-r--r--chromium/base/memory/discardable_memory_malloc.cc43
-rw-r--r--chromium/base/memory/discardable_memory_malloc.h35
-rw-r--r--chromium/base/memory/discardable_memory_manager.cc263
-rw-r--r--chromium/base/memory/discardable_memory_manager.h206
-rw-r--r--chromium/base/memory/discardable_memory_manager_unittest.cc505
-rw-r--r--chromium/base/memory/discardable_memory_provider.cc215
-rw-r--r--chromium/base/memory/discardable_memory_provider.h143
-rw-r--r--chromium/base/memory/discardable_memory_provider_unittest.cc406
-rw-r--r--chromium/base/memory/discardable_memory_unittest.cc106
-rw-r--r--chromium/base/memory/discardable_memory_win.cc62
-rw-r--r--chromium/base/memory/ref_counted.cc42
-rw-r--r--chromium/base/memory/ref_counted.h47
-rw-r--r--chromium/base/memory/ref_counted_memory.cc3
-rw-r--r--chromium/base/memory/ref_counted_memory.h16
-rw-r--r--chromium/base/memory/ref_counted_memory_unittest.cc16
-rw-r--r--chromium/base/memory/scoped_handle.h50
-rw-r--r--chromium/base/memory/scoped_ptr.h140
-rw-r--r--chromium/base/memory/scoped_ptr_unittest.cc2
-rw-r--r--chromium/base/memory/shared_memory.h86
-rw-r--r--chromium/base/memory/shared_memory_android.cc2
-rw-r--r--chromium/base/memory/shared_memory_nacl.cc7
-rw-r--r--chromium/base/memory/shared_memory_posix.cc83
-rw-r--r--chromium/base/memory/shared_memory_unittest.cc54
-rw-r--r--chromium/base/memory/shared_memory_win.cc27
-rw-r--r--chromium/base/memory/singleton.cc5
-rw-r--r--chromium/base/memory/singleton.h9
-rw-r--r--chromium/base/memory/weak_ptr.h2
-rw-r--r--chromium/base/memory/weak_ptr_unittest.cc4
-rw-r--r--chromium/base/message_loop/incoming_task_queue.cc25
-rw-r--r--chromium/base/message_loop/incoming_task_queue.h11
-rw-r--r--chromium/base/message_loop/message_loop.cc199
-rw-r--r--chromium/base/message_loop/message_loop.h233
-rw-r--r--chromium/base/message_loop/message_loop_test.cc57
-rw-r--r--chromium/base/message_loop/message_loop_test.h4
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc165
-rw-r--r--chromium/base/message_loop/message_pump.cc3
-rw-r--r--chromium/base/message_loop/message_pump.h4
-rw-r--r--chromium/base/message_loop/message_pump_android.cc58
-rw-r--r--chromium/base/message_loop/message_pump_dispatcher.h21
-rw-r--r--chromium/base/message_loop/message_pump_glib.cc163
-rw-r--r--chromium/base/message_loop/message_pump_glib.h23
-rw-r--r--chromium/base/message_loop/message_pump_glib_unittest.cc46
-rw-r--r--chromium/base/message_loop/message_pump_gtk.cc120
-rw-r--r--chromium/base/message_loop/message_pump_gtk.h70
-rw-r--r--chromium/base/message_loop/message_pump_io_ios_unittest.cc2
-rw-r--r--chromium/base/message_loop/message_pump_libevent.cc6
-rw-r--r--chromium/base/message_loop/message_pump_libevent_unittest.cc45
-rw-r--r--chromium/base/message_loop/message_pump_mac.h46
-rw-r--r--chromium/base/message_loop/message_pump_mac.mm124
-rw-r--r--chromium/base/message_loop/message_pump_observer.h30
-rw-r--r--chromium/base/message_loop/message_pump_ozone.cc61
-rw-r--r--chromium/base/message_loop/message_pump_ozone.h52
-rw-r--r--chromium/base/message_loop/message_pump_win.cc29
-rw-r--r--chromium/base/message_loop/message_pump_win.h35
-rw-r--r--chromium/base/message_loop/message_pump_x11.cc313
-rw-r--r--chromium/base/message_loop/message_pump_x11.h131
-rw-r--r--chromium/base/message_loop/timer_slack.h22
-rw-r--r--chromium/base/metrics/OWNERS9
-rw-r--r--chromium/base/metrics/field_trial.cc15
-rw-r--r--chromium/base/metrics/field_trial.h14
-rw-r--r--chromium/base/metrics/field_trial_unittest.cc74
-rw-r--r--chromium/base/metrics/histogram.cc22
-rw-r--r--chromium/base/metrics/histogram.h22
-rw-r--r--chromium/base/metrics/histogram_base.cc6
-rw-r--r--chromium/base/metrics/histogram_base.h32
-rw-r--r--chromium/base/metrics/histogram_delta_serialization.cc6
-rw-r--r--chromium/base/metrics/histogram_samples.cc22
-rw-r--r--chromium/base/metrics/histogram_samples.h6
-rw-r--r--chromium/base/metrics/histogram_snapshot_manager.cc11
-rw-r--r--chromium/base/metrics/histogram_snapshot_manager.h10
-rw-r--r--chromium/base/metrics/histogram_snapshot_manager_unittest.cc106
-rw-r--r--chromium/base/metrics/histogram_unittest.cc26
-rw-r--r--chromium/base/metrics/sample_vector.cc15
-rw-r--r--chromium/base/metrics/sample_vector.h6
-rw-r--r--chromium/base/metrics/statistics_recorder.h9
-rw-r--r--chromium/base/metrics/stats_table.cc65
-rw-r--r--chromium/base/metrics/stats_table.h33
-rw-r--r--chromium/base/metrics/stats_table_unittest.cc48
-rw-r--r--chromium/base/metrics/user_metrics.cc74
-rw-r--r--chromium/base/metrics/user_metrics.h60
-rw-r--r--chromium/base/metrics/user_metrics_action.h28
-rw-r--r--chromium/base/native_library.h33
-rw-r--r--chromium/base/native_library_mac.mm7
-rw-r--r--chromium/base/native_library_posix.cc8
-rw-r--r--chromium/base/native_library_win.cc31
-rw-r--r--chromium/base/nix/mime_util_xdg.cc616
-rw-r--r--chromium/base/nix/mime_util_xdg.h11
-rw-r--r--chromium/base/nix/xdg_util.cc13
-rw-r--r--chromium/base/numerics/OWNERS3
-rw-r--r--chromium/base/numerics/safe_conversions.h64
-rw-r--r--chromium/base/numerics/safe_conversions_impl.h217
-rw-r--r--chromium/base/numerics/safe_math.h271
-rw-r--r--chromium/base/numerics/safe_math_impl.h502
-rw-r--r--chromium/base/numerics/safe_numerics_unittest.cc580
-rw-r--r--chromium/base/observer_list_unittest.cc4
-rw-r--r--chromium/base/os_compat_android.cc11
-rw-r--r--chromium/base/path_service.cc17
-rw-r--r--chromium/base/path_service.h14
-rw-r--r--chromium/base/path_service_unittest.cc40
-rw-r--r--chromium/base/pickle.cc41
-rw-r--r--chromium/base/pickle.h29
-rw-r--r--chromium/base/pickle_unittest.cc2
-rw-r--r--chromium/base/platform_file.cc14
-rw-r--r--chromium/base/platform_file.h23
-rw-r--r--chromium/base/platform_file_posix.cc106
-rw-r--r--chromium/base/platform_file_unittest.cc22
-rw-r--r--chromium/base/platform_file_win.cc113
-rw-r--r--chromium/base/port.h20
-rw-r--r--chromium/base/posix/file_descriptor_shuffle.cc12
-rw-r--r--chromium/base/posix/file_descriptor_shuffle.h8
-rw-r--r--chromium/base/posix/global_descriptors.h4
-rw-r--r--chromium/base/posix/unix_domain_socket_linux.cc111
-rw-r--r--chromium/base/posix/unix_domain_socket_linux.h23
-rw-r--r--chromium/base/posix/unix_domain_socket_linux_unittest.cc90
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source_win.cc4
-rw-r--r--chromium/base/prefs/OWNERS1
-rw-r--r--chromium/base/prefs/json_pref_store.cc197
-rw-r--r--chromium/base/prefs/json_pref_store.h72
-rw-r--r--chromium/base/prefs/json_pref_store_unittest.cc435
-rw-r--r--chromium/base/prefs/overlay_user_pref_store_unittest.cc68
-rw-r--r--chromium/base/prefs/persistent_pref_store.h49
-rw-r--r--chromium/base/prefs/pref_filter.h55
-rw-r--r--chromium/base/prefs/pref_notifier_impl_unittest.cc7
-rw-r--r--chromium/base/prefs/pref_registry.cc12
-rw-r--r--chromium/base/prefs/pref_registry_simple.cc13
-rw-r--r--chromium/base/prefs/pref_service.cc18
-rw-r--r--chromium/base/prefs/pref_service_factory.cc4
-rw-r--r--chromium/base/prefs/pref_service_unittest.cc10
-rw-r--r--chromium/base/prefs/pref_store_observer_mock.cc22
-rw-r--r--chromium/base/prefs/pref_store_observer_mock.h20
-rw-r--r--chromium/base/prefs/scoped_user_pref_update.cc2
-rw-r--r--chromium/base/prefs/scoped_user_pref_update_unittest.cc12
-rw-r--r--chromium/base/prefs/testing_pref_store.cc61
-rw-r--r--chromium/base/prefs/testing_pref_store.h28
-rw-r--r--chromium/base/prefs/value_map_pref_store.cc13
-rw-r--r--chromium/base/prefs/value_map_pref_store.h19
-rw-r--r--chromium/base/prefs/writeable_pref_store.h47
-rw-r--r--chromium/base/process/internal_linux.cc13
-rw-r--r--chromium/base/process/internal_linux.h15
-rw-r--r--chromium/base/process/kill_mac.cc11
-rw-r--r--chromium/base/process/kill_posix.cc63
-rw-r--r--chromium/base/process/kill_win.cc10
-rw-r--r--chromium/base/process/launch.cc13
-rw-r--r--chromium/base/process/launch.h48
-rw-r--r--chromium/base/process/launch_mac.cc20
-rw-r--r--chromium/base/process/launch_posix.cc104
-rw-r--r--chromium/base/process/launch_win.cc43
-rw-r--r--chromium/base/process/memory.cc30
-rw-r--r--chromium/base/process/memory.h34
-rw-r--r--chromium/base/process/memory_linux.cc40
-rw-r--r--chromium/base/process/memory_mac.mm53
-rw-r--r--chromium/base/process/memory_unittest.cc96
-rw-r--r--chromium/base/process/memory_win.cc11
-rw-r--r--chromium/base/process/process_handle.h4
-rw-r--r--chromium/base/process/process_handle_freebsd.cc1
-rw-r--r--chromium/base/process/process_handle_linux.cc2
-rw-r--r--chromium/base/process/process_handle_mac.cc9
-rw-r--r--chromium/base/process/process_info_linux.cc4
-rw-r--r--chromium/base/process/process_info_mac.cc2
-rw-r--r--chromium/base/process/process_iterator.h4
-rw-r--r--chromium/base/process/process_iterator_freebsd.cc2
-rw-r--r--chromium/base/process/process_iterator_linux.cc4
-rw-r--r--chromium/base/process/process_iterator_mac.cc1
-rw-r--r--chromium/base/process/process_linux.cc18
-rw-r--r--chromium/base/process/process_metrics.cc8
-rw-r--r--chromium/base/process/process_metrics.h16
-rw-r--r--chromium/base/process/process_metrics_freebsd.cc12
-rw-r--r--chromium/base/process/process_metrics_ios.cc4
-rw-r--r--chromium/base/process/process_metrics_linux.cc28
-rw-r--r--chromium/base/process/process_metrics_mac.cc136
-rw-r--r--chromium/base/process/process_metrics_openbsd.cc17
-rw-r--r--chromium/base/process/process_metrics_posix.cc17
-rw-r--r--chromium/base/process/process_metrics_win.cc15
-rw-r--r--chromium/base/process/process_util_unittest.cc104
-rw-r--r--chromium/base/rand_util.cc10
-rw-r--r--chromium/base/rand_util.h3
-rw-r--r--chromium/base/rand_util_nacl.cc48
-rw-r--r--chromium/base/rand_util_posix.cc21
-rw-r--r--chromium/base/rand_util_unittest.cc22
-rw-r--r--chromium/base/rand_util_win.cc40
-rw-r--r--chromium/base/run_loop.cc12
-rw-r--r--chromium/base/run_loop.h21
-rw-r--r--chromium/base/safe_numerics.h135
-rw-r--r--chromium/base/safe_numerics_unittest.cc151
-rw-r--r--chromium/base/safe_numerics_unittest.nc29
-rw-r--r--chromium/base/scoped_generic.h176
-rw-r--r--chromium/base/scoped_generic_unittest.cc153
-rw-r--r--chromium/base/scoped_observer.h16
-rw-r--r--chromium/base/security_unittest.cc18
-rw-r--r--chromium/base/sequence_checker.h8
-rw-r--r--chromium/base/sequence_checker_unittest.cc9
-rw-r--r--chromium/base/sha1_win.cc10
-rw-r--r--chromium/base/stl_util.h43
-rw-r--r--chromium/base/stl_util_unittest.cc160
-rw-r--r--chromium/base/strings/safe_sprintf_unittest.cc5
-rw-r--r--chromium/base/strings/string16.h5
-rw-r--r--chromium/base/strings/string_number_conversions.cc21
-rw-r--r--chromium/base/strings/string_piece.cc350
-rw-r--r--chromium/base/strings/string_piece.h328
-rw-r--r--chromium/base/strings/string_piece_unittest.cc282
-rw-r--r--chromium/base/strings/string_split.cc197
-rw-r--r--chromium/base/strings/string_split.h21
-rw-r--r--chromium/base/strings/string_split_unittest.cc180
-rw-r--r--chromium/base/strings/string_util.cc145
-rw-r--r--chromium/base/strings/string_util.h77
-rw-r--r--chromium/base/strings/string_util_constants.cc64
-rw-r--r--chromium/base/strings/string_util_posix.h1
-rw-r--r--chromium/base/strings/string_util_unittest.cc56
-rw-r--r--chromium/base/strings/stringprintf.cc10
-rw-r--r--chromium/base/strings/utf_offset_string_conversions.cc300
-rw-r--r--chromium/base/strings/utf_offset_string_conversions.h131
-rw-r--r--chromium/base/strings/utf_offset_string_conversions_unittest.cc178
-rw-r--r--chromium/base/strings/utf_string_conversions.cc5
-rw-r--r--chromium/base/strings/utf_string_conversions.h27
-rw-r--r--chromium/base/supports_user_data.cc5
-rw-r--r--chromium/base/supports_user_data_unittest.cc39
-rw-r--r--chromium/base/sync_socket_posix.cc2
-rw-r--r--chromium/base/synchronization/condition_variable_posix.cc12
-rw-r--r--chromium/base/synchronization/condition_variable_unittest.cc5
-rw-r--r--chromium/base/synchronization/lock.cc21
-rw-r--r--chromium/base/synchronization/lock.h6
-rw-r--r--chromium/base/synchronization/waitable_event_win.cc2
-rw-r--r--chromium/base/sys_byteorder.h59
-rw-r--r--chromium/base/sys_info.h11
-rw-r--r--chromium/base/sys_info_android.cc52
-rw-r--r--chromium/base/sys_info_chromeos.cc3
-rw-r--r--chromium/base/sys_info_ios.mm19
-rw-r--r--chromium/base/sys_info_mac.cc4
-rw-r--r--chromium/base/sys_info_posix.cc20
-rw-r--r--chromium/base/sys_info_unittest.cc2
-rw-r--r--chromium/base/sys_info_win.cc5
-rw-r--r--chromium/base/task/cancelable_task_tracker.cc187
-rw-r--r--chromium/base/task/cancelable_task_tracker.h120
-rw-r--r--chromium/base/task/cancelable_task_tracker_unittest.cc435
-rw-r--r--chromium/base/task_runner_util_unittest.cc10
-rw-r--r--chromium/base/third_party/dmg_fp/dtoa.cc6
-rw-r--r--chromium/base/third_party/dynamic_annotations/BUILD.gn11
-rw-r--r--chromium/base/third_party/nspr/BUILD.gn15
-rw-r--r--chromium/base/third_party/nspr/nspr.gyp41
-rw-r--r--chromium/base/third_party/nspr/prcpucfg.h51
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_freebsd.h337
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_linux.h756
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_mac.h197
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_nacl.h246
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_openbsd.h337
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_solaris.h203
-rw-r--r--chromium/base/third_party/nspr/prcpucfg_win.h256
-rw-r--r--chromium/base/third_party/nspr/prtime.cc120
-rw-r--r--chromium/base/third_party/nspr/prtime.h22
-rw-r--r--chromium/base/third_party/nspr/prtypes.h558
-rw-r--r--chromium/base/third_party/superfasthash/LICENSE27
-rw-r--r--chromium/base/third_party/superfasthash/OWNERS2
-rw-r--r--chromium/base/third_party/superfasthash/README.chromium29
-rw-r--r--chromium/base/third_party/superfasthash/superfasthash.c84
-rw-r--r--chromium/base/third_party/symbolize/BUILD.gn20
-rw-r--r--chromium/base/third_party/symbolize/README.chromium5
-rw-r--r--chromium/base/third_party/symbolize/demangle.cc379
-rw-r--r--chromium/base/third_party/symbolize/symbolize.cc197
-rw-r--r--chromium/base/third_party/symbolize/symbolize.h22
-rw-r--r--chromium/base/third_party/xdg_mime/BUILD.gn28
-rw-r--r--chromium/base/third_party/xdg_user_dirs/BUILD.gn11
-rw-r--r--chromium/base/threading/non_thread_safe.h4
-rw-r--r--chromium/base/threading/platform_thread.h46
-rw-r--r--chromium/base/threading/platform_thread_android.cc19
-rw-r--r--chromium/base/threading/platform_thread_freebsd.cc103
-rw-r--r--chromium/base/threading/platform_thread_linux.cc11
-rw-r--r--chromium/base/threading/platform_thread_mac.mm57
-rw-r--r--chromium/base/threading/platform_thread_posix.cc13
-rw-r--r--chromium/base/threading/platform_thread_win.cc48
-rw-r--r--chromium/base/threading/sequenced_worker_pool.cc17
-rw-r--r--chromium/base/threading/sequenced_worker_pool_unittest.cc12
-rw-r--r--chromium/base/threading/thread.cc2
-rw-r--r--chromium/base/threading/thread.h2
-rw-r--r--chromium/base/threading/thread_checker.h2
-rw-r--r--chromium/base/threading/thread_checker_impl.cc12
-rw-r--r--chromium/base/threading/thread_checker_impl.h2
-rw-r--r--chromium/base/threading/thread_collision_warner.h1
-rw-r--r--chromium/base/threading/thread_local.h6
-rw-r--r--chromium/base/threading/thread_local_android.cc34
-rw-r--r--chromium/base/threading/thread_local_posix.cc4
-rw-r--r--chromium/base/threading/thread_local_storage.cc250
-rw-r--r--chromium/base/threading/thread_local_storage.h71
-rw-r--r--chromium/base/threading/thread_local_storage_posix.cc43
-rw-r--r--chromium/base/threading/thread_local_storage_win.cc202
-rw-r--r--chromium/base/threading/thread_perftest.cc314
-rw-r--r--chromium/base/threading/worker_pool_posix.cc2
-rw-r--r--chromium/base/threading/worker_pool_win.cc4
-rw-r--r--chromium/base/time/pr_time_unittest.cc145
-rw-r--r--chromium/base/time/time.cc50
-rw-r--r--chromium/base/time/time.h76
-rw-r--r--chromium/base/time/time_mac.cc17
-rw-r--r--chromium/base/time/time_posix.cc20
-rw-r--r--chromium/base/time/time_unittest.cc76
-rw-r--r--chromium/base/time/time_win.cc45
-rw-r--r--chromium/base/time/time_win_unittest.cc3
-rw-r--r--chromium/base/timer/elapsed_timer.h3
-rw-r--r--chromium/base/timer/mock_timer.cc63
-rw-r--r--chromium/base/timer/mock_timer.h41
-rw-r--r--chromium/base/timer/mock_timer_unittest.cc82
-rw-r--r--chromium/base/timer/timer.cc8
-rw-r--r--chromium/base/timer/timer.h29
-rw-r--r--chromium/base/timer/timer_unittest.cc16
-rw-r--r--chromium/base/tools_sanity_unittest.cc76
-rw-r--r--chromium/base/tracked_objects.cc5
-rw-r--r--chromium/base/tracked_objects.h1
-rw-r--r--chromium/base/values.cc56
-rw-r--r--chromium/base/values.h27
-rw-r--r--chromium/base/values_unittest.cc304
-rw-r--r--chromium/base/win/OWNERS1
-rw-r--r--chromium/base/win/dllmain.cc6
-rw-r--r--chromium/base/win/event_trace_consumer.h2
-rw-r--r--chromium/base/win/event_trace_consumer_unittest.cc66
-rw-r--r--chromium/base/win/event_trace_controller.cc2
-rw-r--r--chromium/base/win/iat_patch_function.cc20
-rw-r--r--chromium/base/win/iat_patch_function.h2
-rw-r--r--chromium/base/win/message_window.cc4
-rw-r--r--chromium/base/win/metro.cc38
-rw-r--r--chromium/base/win/metro.h11
-rw-r--r--chromium/base/win/object_watcher.cc4
-rw-r--r--chromium/base/win/registry.cc189
-rw-r--r--chromium/base/win/registry.h20
-rw-r--r--chromium/base/win/registry_unittest.cc201
-rw-r--r--chromium/base/win/scoped_com_initializer.h5
-rw-r--r--chromium/base/win/scoped_gdi_object.h2
-rw-r--r--chromium/base/win/scoped_handle.h6
-rw-r--r--chromium/base/win/scoped_process_information_unittest.cc3
-rw-r--r--chromium/base/win/shortcut.cc133
-rw-r--r--chromium/base/win/shortcut.h49
-rw-r--r--chromium/base/win/shortcut_unittest.cc59
-rw-r--r--chromium/base/win/startup_information_unittest.cc2
-rw-r--r--chromium/base/win/text_services_message_filter.cc82
-rw-r--r--chromium/base/win/text_services_message_filter.h48
-rw-r--r--chromium/base/win/win_util.cc82
-rw-r--r--chromium/base/win/win_util.h15
-rw-r--r--chromium/base/x11/edid_parser_x11.cc196
-rw-r--r--chromium/base/x11/edid_parser_x11.h54
-rw-r--r--chromium/base/x11/edid_parser_x11_unittest.cc167
-rw-r--r--chromium/base/x11/x11_error_tracker.cc37
-rw-r--r--chromium/base/x11/x11_error_tracker.h38
-rw-r--r--chromium/base/x11/x11_error_tracker_gtk.cc29
587 files changed, 27035 insertions, 16258 deletions
diff --git a/chromium/base/BUILD.gn b/chromium/base/BUILD.gn
new file mode 100644
index 00000000000..53084d5e8f6
--- /dev/null
+++ b/chromium/base/BUILD.gn
@@ -0,0 +1,1373 @@
+# 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.
+
+import("//build/config/android/rules.gni")
+import("//build/config/ui.gni")
+
+component("base") {
+ sources = [
+ "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/superfasthash/superfasthash.c",
+ "allocator/allocator_extension.cc",
+ "allocator/allocator_extension.h",
+ "allocator/type_profiler_control.cc",
+ "allocator/type_profiler_control.h",
+ "android/application_status_listener.cc",
+ "android/application_status_listener.h",
+ "android/base_jni_registrar.cc",
+ "android/base_jni_registrar.h",
+ "android/build_info.cc",
+ "android/build_info.h",
+ "android/command_line_android.cc",
+ "android/command_line_android.h",
+ "android/content_uri_utils.cc",
+ "android/content_uri_utils.h",
+ "android/cpu_features.cc",
+ "android/event_log.cc",
+ "android/event_log.h",
+ "android/fifo_utils.cc",
+ "android/fifo_utils.h",
+ "android/important_file_writer_android.cc",
+ "android/important_file_writer_android.h",
+ "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_registrar.cc",
+ "android/jni_registrar.h",
+ "android/jni_string.cc",
+ "android/jni_string.h",
+ "android/jni_weak_ref.cc",
+ "android/jni_weak_ref.h",
+ "android/library_loader/library_loader_hooks.cc",
+ "android/library_loader/library_loader_hooks.h",
+ "android/memory_pressure_listener_android.cc",
+ "android/memory_pressure_listener_android.h",
+ "android/java_handler_thread.cc",
+ "android/java_handler_thread.h",
+ "android/path_service_android.cc",
+ "android/path_service_android.h",
+ "android/path_utils.cc",
+ "android/path_utils.h",
+ "android/sys_utils.cc",
+ "android/sys_utils.h",
+ "android/thread_utils.h",
+ "android/trace_event_binding.cc",
+ "android/trace_event_binding.h",
+ "async_socket_io_handler.h",
+ "async_socket_io_handler_posix.cc",
+ "async_socket_io_handler_win.cc",
+ "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",
+ "barrier_closure.cc",
+ "barrier_closure.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",
+ "big_endian.cc",
+ "big_endian.h",
+ "bind.h",
+ "bind_helpers.cc",
+ "bind_helpers.h",
+ "bind_internal.h",
+ "bind_internal_win.h",
+ "bits.h",
+ "build_time.cc",
+ "build_time.h",
+ "callback.h",
+ "callback_helpers.cc",
+ "callback_helpers.h",
+ "callback_internal.cc",
+ "callback_internal.h",
+ "cancelable_callback.h",
+ "command_line.cc",
+ "command_line.h",
+ "compiler_specific.h",
+ "containers/hash_tables.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_internal_ios.mm",
+ "debug/alias.cc",
+ "debug/alias.h",
+ "debug/crash_logging.cc",
+ "debug/crash_logging.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/proc_maps_linux.cc",
+ "debug/proc_maps_linux.h",
+ "debug/profiler.cc",
+ "debug/profiler.h",
+ "debug/stack_trace.cc",
+ "debug/stack_trace.h",
+ "debug/stack_trace_android.cc",
+ "debug/stack_trace_posix.cc",
+ "debug/stack_trace_win.cc",
+ "debug/trace_event.h",
+ "debug/trace_event_android.cc",
+ "debug/trace_event_impl.cc",
+ "debug/trace_event_impl.h",
+ "debug/trace_event_impl_constants.cc",
+ "debug/trace_event_memory.cc",
+ "debug/trace_event_memory.h",
+ "debug/trace_event_synthetic_delay.cc",
+ "debug/trace_event_synthetic_delay.h",
+ "debug/trace_event_system_stats_monitor.cc",
+ "debug/trace_event_system_stats_monitor.h",
+ "debug/trace_event_win.cc",
+ "deferred_sequenced_task_runner.cc",
+ "deferred_sequenced_task_runner.h",
+ "environment.cc",
+ "environment.h",
+ "file_descriptor_posix.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_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_linux.h",
+ "files/dir_reader_posix.h",
+ "files/file.cc",
+ "files/file_posix.cc",
+ "files/file_win.cc",
+ "files/file_enumerator.cc",
+ "files/file_enumerator.h",
+ "files/file_enumerator_posix.cc",
+ "files/file_enumerator_win.cc",
+ "files/file_path.cc",
+ "files/file_path.h",
+ "files/file_path_constants.cc",
+ "files/file_path_watcher.cc",
+ "files/file_path_watcher.h",
+ "files/file_path_watcher_fsevents.cc",
+ "files/file_path_watcher_fsevents.h",
+ "files/file_path_watcher_kqueue.cc",
+ "files/file_path_watcher_kqueue.h",
+ "files/file_path_watcher_linux.cc",
+ "files/file_path_watcher_mac.cc",
+ "files/file_path_watcher_win.cc",
+ "files/file_proxy.cc",
+ "files/file_proxy.h",
+ "files/file_util_proxy.cc",
+ "files/file_util_proxy.h",
+ "files/important_file_writer.cc",
+ "files/important_file_writer.h",
+ "files/memory_mapped_file.cc",
+ "files/memory_mapped_file.h",
+ "files/memory_mapped_file_posix.cc",
+ "files/memory_mapped_file_win.cc",
+ "files/scoped_file.cc",
+ "files/scoped_file.h",
+ "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",
+ "id_map.h",
+ "ini_parser.cc",
+ "ini_parser.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/bundle_locations.h",
+ "mac/bundle_locations.mm",
+ "mac/cocoa_protocols.h",
+ "mac/foundation_util.h",
+ "mac/foundation_util.mm",
+ "mac/launch_services_util.cc",
+ "mac/launch_services_util.h",
+ "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/mach_logging.cc",
+ "mac/mach_logging.h",
+ "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_block.h",
+ "mac/scoped_cftyperef.h",
+ "mac/scoped_ioobject.h",
+ "mac/scoped_ioplugininterface.h",
+ "mac/scoped_launch_data.h",
+ "mac/scoped_mach_port.cc",
+ "mac/scoped_mach_port.h",
+ "mac/scoped_mach_vm.cc",
+ "mac/scoped_mach_vm.h",
+ "mac/scoped_nsautorelease_pool.h",
+ "mac/scoped_nsautorelease_pool.mm",
+ "mac/scoped_nsexception_enabler.h",
+ "mac/scoped_nsexception_enabler.mm",
+ "mac/scoped_nsobject.h",
+ "mac/scoped_sending_event.h",
+ "mac/scoped_sending_event.mm",
+ "mac/sdk_forward_declarations.h",
+ "macros.h",
+ "md5.cc",
+ "md5.h",
+ "memory/aligned_memory.cc",
+ "memory/aligned_memory.h",
+ "memory/discardable_memory.cc",
+ "memory/discardable_memory.h",
+ "memory/discardable_memory_android.cc",
+ "memory/discardable_memory_emulated.cc",
+ "memory/discardable_memory_emulated.h",
+ "memory/discardable_memory_linux.cc",
+ "memory/discardable_memory_mac.cc",
+ "memory/discardable_memory_malloc.cc",
+ "memory/discardable_memory_malloc.h",
+ "memory/discardable_memory_manager.cc",
+ "memory/discardable_memory_manager.h",
+ "memory/discardable_memory_win.cc",
+ "memory/linked_ptr.h",
+ "memory/manual_constructor.h",
+ "memory/memory_pressure_listener.cc",
+ "memory/memory_pressure_listener.h",
+ "memory/raw_scoped_refptr_mismatch_checker.h",
+ "memory/ref_counted.cc",
+ "memory/ref_counted.h",
+ "memory/ref_counted_delete_on_message_loop.h",
+ "memory/ref_counted_memory.cc",
+ "memory/ref_counted_memory.h",
+ "memory/scoped_handle.h",
+ "memory/scoped_open_process.h",
+ "memory/scoped_policy.h",
+ "memory/scoped_ptr.h",
+ "memory/scoped_vector.h",
+ "memory/shared_memory.h",
+ "memory/shared_memory_android.cc",
+ "memory/shared_memory_nacl.cc",
+ "memory/shared_memory_posix.cc",
+ "memory/shared_memory_win.cc",
+ "memory/singleton.cc",
+ "memory/singleton.h",
+ "memory/weak_ptr.cc",
+ "memory/weak_ptr.h",
+ "message_loop/incoming_task_queue.cc",
+ "message_loop/incoming_task_queue.h",
+ "message_loop/message_loop.cc",
+ "message_loop/message_loop.h",
+ "message_loop/message_loop_proxy.cc",
+ "message_loop/message_loop_proxy.h",
+ "message_loop/message_loop_proxy_impl.cc",
+ "message_loop/message_loop_proxy_impl.h",
+ "message_loop/message_pump.cc",
+ "message_loop/message_pump.h",
+ "message_loop/message_pump_android.cc",
+ "message_loop/message_pump_android.h",
+ "message_loop/message_pump_default.cc",
+ "message_loop/message_pump_default.h",
+ "message_loop/message_pump_glib.cc",
+ "message_loop/message_pump_glib.h",
+ "message_loop/message_pump_io_ios.cc",
+ "message_loop/message_pump_io_ios.h",
+ "message_loop/message_pump_libevent.cc",
+ "message_loop/message_pump_libevent.h",
+ "message_loop/message_pump_mac.h",
+ "message_loop/message_pump_mac.mm",
+ "message_loop/message_pump_observer.h",
+ "message_loop/message_pump_win.cc",
+ "message_loop/message_pump_win.h",
+ "metrics/field_trial.cc",
+ "metrics/field_trial.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_delta_serialization.cc",
+ "metrics/histogram_delta_serialization.",
+ "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",
+ "metrics/user_metrics.cc",
+ "metrics/user_metrics.h",
+ "metrics/user_metrics_action.h",
+ "move.h",
+ "native_library.h",
+ "native_library_mac.mm",
+ "native_library_posix.cc",
+ "native_library_win.cc",
+ "numerics/safe_conversions.h",
+ "numerics/safe_conversions_impl.h",
+ "numerics/safe_math.h",
+ "numerics/safe_math_impl.h",
+ "nix/mime_util_xdg.cc",
+ "nix/mime_util_xdg.h",
+ "nix/xdg_util.cc",
+ "nix/xdg_util.h",
+ "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/file_descriptor_shuffle.cc",
+ "posix/global_descriptors.cc",
+ "posix/global_descriptors.h",
+ "posix/unix_domain_socket_linux.cc",
+ "posix/unix_domain_socket_linux.h",
+ "power_monitor/power_monitor.cc",
+ "power_monitor/power_monitor.h",
+ "power_monitor/power_monitor_device_source.cc",
+ "power_monitor/power_monitor_device_source.h",
+ "power_monitor/power_monitor_device_source_android.cc",
+ "power_monitor/power_monitor_device_source_android.h",
+ "power_monitor/power_monitor_device_source_ios.mm",
+ "power_monitor/power_monitor_device_source_mac.mm",
+ "power_monitor/power_monitor_device_source_posix.cc",
+ "power_monitor/power_monitor_device_source_win.cc",
+ "power_monitor/power_monitor_source.cc",
+ "power_monitor/power_monitor_source.h",
+ "power_monitor/power_observer.h",
+ "process/internal_linux.cc",
+ "process/internal_linux.h",
+ "process/kill.cc",
+ "process/kill.h",
+ "process/kill_mac.cc",
+ "process/kill_posix.cc",
+ "process/kill_win.cc",
+ "process/launch.cc",
+ "process/launch.h",
+ "process/launch_ios.cc",
+ "process/launch_mac.cc",
+ "process/launch_posix.cc",
+ "process/launch_win.cc",
+ "process/memory.cc",
+ "process/memory.h",
+ "process/memory_linux.cc",
+ "process/memory_mac.mm",
+ "process/memory_win.cc",
+ "process/process.h",
+ "process/process_handle_freebsd.cc",
+ "process/process_handle_linux.cc",
+ "process/process_handle_mac.cc",
+ "process/process_handle_openbsd.cc",
+ "process/process_handle_posix.cc",
+ "process/process_handle_win.cc",
+ "process/process_info.h",
+ "process/process_info_mac.cc",
+ "process/process_info_win.cc",
+ "process/process_iterator.cc",
+ "process/process_iterator.h",
+ "process/process_iterator_freebsd.cc",
+ "process/process_iterator_linux.cc",
+ "process/process_iterator_mac.cc",
+ "process/process_iterator_openbsd.cc",
+ "process/process_iterator_win.cc",
+ "process/process_linux.cc",
+ "process/process_metrics.cc",
+ "process/process_metrics.h",
+ "process/process_metrics_freebsd.cc",
+ "process/process_metrics_ios.cc",
+ "process/process_metrics_linux.cc",
+ "process/process_metrics_mac.cc",
+ "process/process_metrics_openbsd.cc",
+ "process/process_metrics_posix.cc",
+ "process/process_metrics_win.cc",
+ "process/process_posix.cc",
+ "process/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_generic.h",
+ "scoped_native_library.cc",
+ "scoped_native_library.h",
+ "sequence_checker.h",
+ "sequence_checker_impl.cc",
+ "sequence_checker_impl.h",
+ "sequenced_task_runner.cc",
+ "sequenced_task_runner.h",
+ "sequenced_task_runner_helpers.h",
+ "sha1.h",
+ "sha1_portable.cc",
+ "sha1_win.cc",
+ "single_thread_task_runner.h",
+ "stl_util.h",
+ "strings/latin1_string_conversions.cc",
+ "strings/latin1_string_conversions.h",
+ "strings/nullable_string16.cc",
+ "strings/nullable_string16.h",
+ "strings/safe_sprintf.cc",
+ "strings/safe_sprintf.h",
+ "strings/string16.cc",
+ "strings/string16.h",
+ "strings/string_number_conversions.cc",
+ "strings/string_split.cc",
+ "strings/string_split.h",
+ "strings/string_number_conversions.h",
+ "strings/string_piece.cc",
+ "strings/string_piece.h",
+ "strings/string_tokenizer.h",
+ "strings/string_util.cc",
+ "strings/string_util.h",
+ "strings/string_util_constants.cc",
+ "strings/string_util_posix.h",
+ "strings/string_util_win.h",
+ "strings/stringize_macros.h",
+ "strings/stringprintf.cc",
+ "strings/stringprintf.h",
+ "strings/sys_string_conversions.h",
+ "strings/sys_string_conversions_mac.mm",
+ "strings/sys_string_conversions_posix.cc",
+ "strings/sys_string_conversions_win.cc",
+ "strings/utf_offset_string_conversions.cc",
+ "strings/utf_offset_string_conversions.h",
+ "strings/utf_string_conversion_utils.cc",
+ "strings/utf_string_conversion_utils.h",
+ "strings/utf_string_conversions.cc",
+ "strings/utf_string_conversions.h",
+ "supports_user_data.cc",
+ "supports_user_data.h",
+ "sync_socket.h",
+ "sync_socket_posix.cc",
+ "sync_socket_win.cc",
+ "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/spin_wait.h",
+ "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",
+ "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",
+ "task/cancelable_task_tracker.cc",
+ "task/cancelable_task_tracker.h",
+ "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_android.cc",
+ "threading/platform_thread_linux.cc",
+ "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_id_name_manager.cc",
+ "threading/thread_id_name_manager.h",
+ "threading/thread_local.h",
+ "threading/thread_local_android.cc",
+ "threading/thread_local_posix.cc",
+ "threading/thread_local_storage.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/clock.cc",
+ "time/clock.h",
+ "time/default_clock.cc",
+ "time/default_clock.h",
+ "time/default_tick_clock.cc",
+ "time/default_tick_clock.h",
+ "time/tick_clock.cc",
+ "time/tick_clock.h",
+ "time/time.cc",
+ "time/time.h",
+ "time/time_mac.cc",
+ "time/time_posix.cc",
+ "time/time_win.cc",
+ "timer/elapsed_timer.cc",
+ "timer/elapsed_timer.h",
+ "timer/hi_res_timer_manager.h",
+ "timer/hi_res_timer_manager_posix.cc",
+ "timer/hi_res_timer_manager_win.cc",
+ "timer/mock_timer.cc",
+ "timer/mock_timer.h",
+ "timer/timer.cc",
+ "timer/timer.h",
+ "tracked_objects.cc",
+ "tracked_objects.h",
+ "tracking_info.cc",
+ "tracking_info.h",
+ "tuple.h",
+ "values.cc",
+ "values.h",
+ "value_conversions.cc",
+ "value_conversions.h",
+ "version.cc",
+ "version.h",
+ "vlog.cc",
+ "vlog.h",
+ "win/enum_variant.cc",
+ "win/enum_variant.h",
+ "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.cc",
+ "win/iunknown_impl.h",
+ "win/message_window.cc",
+ "win/message_window.h",
+ "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/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_propvariant.h",
+ "win/scoped_select_object.h",
+ "win/scoped_variant.cc",
+ "win/scoped_variant.h",
+ "win/shortcut.cc",
+ "win/shortcut.h",
+ "win/startup_information.cc",
+ "win/startup_information.h",
+ "win/win_util.cc",
+ "win/win_util.h",
+ "win/windows_version.cc",
+ "win/windows_version.h",
+ "win/wrapped_window_proc.cc",
+ "win/wrapped_window_proc.h",
+ ]
+
+ if (is_nacl) {
+ sources += [ "files/file_path_watcher_stub.cc" ]
+ }
+
+ sources -= [
+ "process/process_handle_freebsd.cc",
+ "process/process_handle_openbsd.cc",
+ "process/process_iterator_freebsd.cc",
+ "process/process_iterator_openbsd.cc",
+ "process/process_metrics_freebsd.cc",
+ "process/process_metrics_openbsd.cc",
+ "sys_info_freebsd.cc",
+ "sys_info_openbsd.cc",
+ ]
+
+ defines = [
+ "BASE_IMPLEMENTATION",
+ ]
+
+ deps = [
+ ":base_static",
+ "//base/allocator:allocator_extension_thunks",
+ "//base/third_party/dynamic_annotations",
+ "//base/third_party/nspr",
+ "//third_party/modp_b64",
+ ]
+
+ if (is_android) {
+ sources += [
+ "memory/discardable_memory_ashmem_allocator.cc",
+ "memory/discardable_memory_ashmem_allocator.h",
+ "memory/discardable_memory_ashmem.cc",
+ "memory/discardable_memory_ashmem.h",
+ ]
+ sources -= [
+ "base_paths_posix.cc",
+ "power_monitor/power_monitor_device_source_posix.cc",
+ ]
+
+ # Android uses some Linux sources, put those back.
+ set_sources_assignment_filter([])
+ sources += [
+ "debug/proc_maps_linux.cc",
+ "files/file_path_watcher_linux.cc",
+ "process/memory_linux.cc",
+ "process/internal_linux.cc",
+ "process/process_handle_linux.cc",
+ "process/process_iterator_linux.cc",
+ "process/process_metrics_linux.cc",
+ "posix/unix_domain_socket_linux.cc",
+ "sys_info_linux.cc",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+
+ deps += [
+ ":base_jni_headers",
+ "//third_party/ashmem",
+ "//third_party/android_tools:cpu_features"
+ ]
+
+ # logging.cc uses the Android logging library.
+ libs = [ "log" ]
+
+ sources -= [
+ "debug/stack_trace_posix.cc",
+ ]
+ }
+
+ if (is_nacl) {
+ # These things would otherwise be built on a Posix build but aren't
+ # supported on NaCl.
+ sources -= [
+ "debug/stack_trace_posix.cc",
+ "files/file_enumerator_posix.cc",
+ "file_util_posix.cc",
+ "message_loop/message_pump_libevent.cc",
+ "process/kill_posix.cc",
+ "process/launch_posix.cc",
+ "process/process_metrics_posix.cc",
+ "process/process_posix.cc",
+ "metrics/field_trial.cc",
+ "native_library_posix.cc",
+ "memory/shared_memory_posix.cc",
+ "sync_socket_posix.cc",
+ "sys_info_posix.cc",
+ ]
+ } else {
+ # Remove nacl stuff.
+ sources -= [
+ "os_compat_nacl.cc",
+ "os_compat_nacl.h",
+ "rand_util_nacl.cc",
+ "memory/shared_memory_nacl.cc",
+ ]
+ }
+
+ # Windows.
+ if (is_win) {
+ sources -= [
+ "message_loop/message_pump_libevent.cc",
+ "strings/string16.cc",
+ # Not using sha1_win.cc because it may have caused a
+ # regression to page cycler moz.
+ "sha1_win.cc",
+ ]
+
+ libs = [
+ "netapi32.lib",
+ "powrprof.lib",
+ ]
+ ldflags = [
+ "/DELAYLOAD:powrprof.dll",
+ ]
+ } else if (!is_nacl) {
+ # Non-Windows.
+ deps += [ "//third_party/libevent" ]
+ }
+
+ # Mac.
+ if (is_mac) {
+ sources -= [
+ "base_paths_posix.cc",
+ "native_library_posix.cc",
+ "strings/sys_string_conversions_posix.cc",
+ ]
+ deps += [ "//third_party/mach_override" ]
+ } else {
+ # Non-Mac.
+ sources -= [
+ "files/file_path_watcher_fsevents.cc",
+ "files/file_path_watcher_fsevents.h",
+ "files/file_path_watcher_kqueue.cc",
+ "files/file_path_watcher_kqueue.h",
+ ]
+ }
+
+ # Linux.
+ if (is_linux) {
+ # TODO(brettw) this will need to be parameterized at some point.
+ linux_configs = [
+ "//build/config/linux:glib",
+ ]
+
+ configs += linux_configs
+ all_dependent_configs = linux_configs
+
+ # These dependencies are not required on Android, and in the case
+ # of xdg_mime must be excluded due to licensing restrictions.
+ deps += [
+ "//base/third_party/symbolize",
+ "//base/third_party/xdg_mime",
+ "//base/third_party/xdg_user_dirs",
+ ]
+ } else {
+ # Non-Linux.
+ sources -= [
+ "nix/mime_util_xdg.cc",
+ "nix/mime_util_xdg.h",
+ "nix/xdg_util.cc",
+ "nix/xdg_util.h",
+ ]
+ }
+
+ if (!use_glib) {
+ sources -= [
+ "message_loop/message_pump_glib.cc",
+ "message_loop/message_pump_glib.h",
+ ]
+ }
+}
+
+# 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.
+source_set("base_static") {
+ sources = [
+ "base_switches.cc",
+ "base_switches.h",
+ "win/pe_image.cc",
+ "win/pe_image.h",
+ ]
+}
+
+component("i18n") {
+ output_name = "base_i18n"
+ 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/case_conversion.cc",
+ "i18n/case_conversion.h",
+ "i18n/char_iterator.cc",
+ "i18n/char_iterator.h",
+ "i18n/file_util_icu.cc",
+ "i18n/file_util_icu.h",
+ "i18n/i18n_constants.cc",
+ "i18n/i18n_constants.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/streaming_utf8_validator.cc",
+ "i18n/streaming_utf8_validator.h",
+ "i18n/string_compare.cc",
+ "i18n/string_compare.h",
+ "i18n/string_search.cc",
+ "i18n/string_search.h",
+ "i18n/time_formatting.cc",
+ "i18n/time_formatting.h",
+ "i18n/timezone.cc",
+ "i18n/timezone.h",
+ "i18n/utf8_validator_tables.cc",
+ "i18n/utf8_validator_tables.h",
+ ]
+ defines = [ "BASE_I18N_IMPLEMENTATION" ]
+ configs += [ "//build/config/compiler:wexit_time_destructors" ]
+ deps = [
+ ":base",
+ "//base/third_party/dynamic_annotations",
+ "//third_party/icu",
+ ]
+}
+
+source_set("prefs") {
+ sources = [
+ "prefs/base_prefs_export.h",
+ "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_change_registrar.cc",
+ "prefs/pref_change_registrar.h",
+ "prefs/pref_filter.h",
+ "prefs/pref_member.cc",
+ "prefs/pref_member.h",
+ "prefs/pref_notifier.h",
+ "prefs/pref_notifier_impl.cc",
+ "prefs/pref_notifier_impl.h",
+ "prefs/pref_observer.h",
+ "prefs/pref_registry.cc",
+ "prefs/pref_registry.h",
+ "prefs/pref_registry_simple.cc",
+ "prefs/pref_registry_simple.h",
+ "prefs/pref_service.cc",
+ "prefs/pref_service.h",
+ "prefs/pref_service_factory.cc",
+ "prefs/pref_service_factory.h",
+ "prefs/pref_store.cc",
+ "prefs/pref_store.h",
+ "prefs/pref_value_map.cc",
+ "prefs/pref_value_map.h",
+ "prefs/pref_value_store.cc",
+ "prefs/pref_value_store.h",
+ "prefs/scoped_user_pref_update.cc",
+ "prefs/scoped_user_pref_update.h",
+ "prefs/value_map_pref_store.cc",
+ "prefs/value_map_pref_store.h",
+ "prefs/writeable_pref_store.h",
+ ]
+
+ defines = [ "BASE_PREFS_IMPLEMENTATION" ]
+
+ deps = [ ":base" ]
+}
+
+source_set("prefs_test_support") {
+ sources = [
+ "prefs/mock_pref_change_callback.cc",
+ "prefs/pref_store_observer_mock.cc",
+ "prefs/pref_store_observer_mock.h",
+ "prefs/testing_pref_service.cc",
+ "prefs/testing_pref_service.h",
+ "prefs/testing_pref_store.cc",
+ "prefs/testing_pref_store.h",
+ ]
+
+ deps = [
+ ":base",
+ ":prefs",
+ "//testing/gmock",
+ ]
+}
+
+source_set("message_loop_tests") {
+ sources = [
+ "message_loop/message_loop_test.cc",
+ "message_loop/message_loop_test.h",
+ ]
+
+ deps = [
+ ":base",
+ "//testing/gtest",
+ ]
+}
+
+test("base_unittests") {
+ sources = [
+ "android/application_status_listener_unittest.cc",
+ "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",
+ "android/sys_utils_unittest.cc",
+ "async_socket_io_handler_unittest.cc",
+ "at_exit_unittest.cc",
+ "atomicops_unittest.cc",
+ "barrier_closure_unittest.cc",
+ "base64_unittest.cc",
+ "big_endian_unittest.cc",
+ "bind_unittest.cc",
+ "bind_unittest.nc",
+ "bits_unittest.cc",
+ "build_time_unittest.cc",
+ "callback_helpers_unittest.cc",
+ "callback_list_unittest.cc",
+ "callback_list_unittest.nc",
+ "callback_unittest.cc",
+ "callback_unittest.nc",
+ "cancelable_callback_unittest.cc",
+ "command_line_unittest.cc",
+ "containers/hash_tables_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/crash_logging_unittest.cc",
+ "debug/leak_tracker_unittest.cc",
+ "debug/proc_maps_linux_unittest.cc",
+ "debug/stack_trace_unittest.cc",
+ "debug/trace_event_memory_unittest.cc",
+ "debug/trace_event_synthetic_delay_unittest.cc",
+ "debug/trace_event_system_stats_monitor_unittest.cc",
+ "debug/trace_event_unittest.cc",
+ "debug/trace_event_unittest.h",
+ "debug/trace_event_win_unittest.cc",
+ "deferred_sequenced_task_runner_unittest.cc",
+ "environment_unittest.cc",
+ "file_util_unittest.cc",
+ "file_version_info_unittest.cc",
+ "files/dir_reader_posix_unittest.cc",
+ "files/file_path_unittest.cc",
+ "files/file_proxy_unittest.cc",
+ "files/file_unittest.cc",
+ "files/file_util_proxy_unittest.cc",
+ "files/important_file_writer_unittest.cc",
+ "files/scoped_temp_dir_unittest.cc",
+ "gmock_unittest.cc",
+ "guid_unittest.cc",
+ "hash_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/streaming_utf8_validator_unittest.cc",
+ "i18n/string_search_unittest.cc",
+ "i18n/time_formatting_unittest.cc",
+ "i18n/timezone_unittest.cc",
+ "ini_parser_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_nsobject_unittest.mm",
+ "mac/scoped_sending_event_unittest.mm",
+ "md5_unittest.cc",
+ "memory/aligned_memory_unittest.cc",
+ "memory/discardable_memory_manager_unittest.cc",
+ "memory/discardable_memory_unittest.cc",
+ "memory/linked_ptr_unittest.cc",
+ "memory/ref_counted_memory_unittest.cc",
+ "memory/ref_counted_unittest.cc",
+ "memory/scoped_ptr_unittest.cc",
+ "memory/scoped_ptr_unittest.nc",
+ "memory/scoped_vector_unittest.cc",
+ "memory/shared_memory_unittest.cc",
+ "memory/singleton_unittest.cc",
+ "memory/weak_ptr_unittest.cc",
+ "memory/weak_ptr_unittest.nc",
+ "message_loop/message_loop_proxy_impl_unittest.cc",
+ "message_loop/message_loop_proxy_unittest.cc",
+ "message_loop/message_loop_unittest.cc",
+ "message_loop/message_pump_glib_unittest.cc",
+ "message_loop/message_pump_io_ios_unittest.cc",
+ "metrics/sample_map_unittest.cc",
+ "metrics/sample_vector_unittest.cc",
+ "metrics/bucket_ranges_unittest.cc",
+ "metrics/field_trial_unittest.cc",
+ "metrics/histogram_base_unittest.cc",
+ "metrics/histogram_delta_serialization_unittest.cc",
+ "metrics/histogram_snapshot_manager_unittest.cc",
+ "metrics/histogram_unittest.cc",
+ "metrics/sparse_histogram_unittest.cc",
+ "metrics/stats_table_unittest.cc",
+ "metrics/statistics_recorder_unittest.cc",
+ "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",
+ "posix/unix_domain_socket_linux_unittest.cc",
+ "power_monitor/power_monitor_unittest.cc",
+ "prefs/default_pref_store_unittest.cc",
+ "prefs/json_pref_store_unittest.cc",
+ "prefs/mock_pref_change_callback.h",
+ "prefs/overlay_user_pref_store_unittest.cc",
+ "prefs/pref_change_registrar_unittest.cc",
+ "prefs/pref_member_unittest.cc",
+ "prefs/pref_notifier_impl_unittest.cc",
+ "prefs/pref_service_unittest.cc",
+ "prefs/pref_value_map_unittest.cc",
+ "prefs/pref_value_store_unittest.cc",
+ "prefs/scoped_user_pref_update_unittest.cc",
+ "process/memory_unittest.cc",
+ "process/memory_unittest_mac.h",
+ "process/memory_unittest_mac.mm",
+ "process/process_metrics_unittest.cc",
+ "process/process_metrics_unittest_ios.cc",
+ "process/process_util_unittest.cc",
+ "process/process_util_unittest_ios.cc",
+ "profiler/tracked_time_unittest.cc",
+ "rand_util_unittest.cc",
+ "numerics/safe_numerics_unittest.cc",
+ "scoped_clear_errno_unittest.cc",
+ "scoped_generic_unittest.cc",
+ "scoped_native_library_unittest.cc",
+ "scoped_observer.h",
+ "security_unittest.cc",
+ "sequence_checker_unittest.cc",
+ "sha1_unittest.cc",
+ "stl_util_unittest.cc",
+ "strings/nullable_string16_unittest.cc",
+ "strings/safe_sprintf_unittest.cc",
+ "strings/string16_unittest.cc",
+ "strings/stringprintf_unittest.cc",
+ "strings/string_number_conversions_unittest.cc",
+ "strings/string_piece_unittest.cc",
+ "strings/string_split_unittest.cc",
+ "strings/string_tokenizer_unittest.cc",
+ "strings/string_util_unittest.cc",
+ "strings/stringize_macros_unittest.cc",
+ "strings/sys_string_conversions_mac_unittest.mm",
+ "strings/sys_string_conversions_unittest.cc",
+ "strings/utf_offset_string_conversions_unittest.cc",
+ "strings/utf_string_conversions_unittest.cc",
+ "supports_user_data_unittest.cc",
+ "sync_socket_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",
+ "system_monitor/system_monitor_unittest.cc",
+ "task/cancelable_task_tracker_unittest.cc",
+ "task_runner_util_unittest.cc",
+ "template_util_unittest.cc",
+ "test/expectations/expectation_unittest.cc",
+ "test/expectations/parser_unittest.cc",
+ "test/statistics_delta_reader_unittest.cc",
+ "test/test_reg_util_win_unittest.cc",
+ "test/trace_event_analyzer_unittest.cc",
+ "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_id_name_manager_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/pr_time_unittest.cc",
+ "time/time_unittest.cc",
+ "time/time_win_unittest.cc",
+ "timer/hi_res_timer_manager_unittest.cc",
+ "timer/mock_timer_unittest.cc",
+ "timer/timer_unittest.cc",
+ "tools_sanity_unittest.cc",
+ "tracked_objects_unittest.cc",
+ "tuple_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/message_window_unittest.cc",
+ "win/object_watcher_unittest.cc",
+ "win/pe_image_unittest.cc",
+ "win/registry_unittest.cc",
+ "win/scoped_bstr_unittest.cc",
+ "win/scoped_comptr_unittest.cc",
+ "win/scoped_process_information_unittest.cc",
+ "win/scoped_variant_unittest.cc",
+ "win/shortcut_unittest.cc",
+ "win/startup_information_unittest.cc",
+ "win/win_util_unittest.cc",
+ "win/wrapped_window_proc_unittest.cc",
+ ]
+
+ deps = [
+ ":base",
+ ":i18n",
+ ":message_loop_tests",
+ ":prefs",
+ ":prefs_test_support",
+ "//base/allocator",
+ "//base/test:run_all_unittests",
+ "//base/test:test_support",
+ "//base/third_party/dynamic_annotations",
+ "//testing/gmock",
+ "//testing/gtest",
+ "//third_party/icu",
+ ]
+
+ if (is_ios) {
+ sources -= [
+ "metrics/stats_table_uinittest.cc", # Requires spawning a process.
+ "process/memory_unittest.cc",
+ "process/memory_unittest_mac.h",
+ "process/memory_unittest_mac.mm",
+ "process/process_util_unittest.cc",
+ ]
+
+ # Pull in specific Mac files for iOS (which have been filtered out by file
+ # name rules).
+ set_sources_assignment_filter([])
+ sources += [
+ "mac/bind_objc_block_unittest.mm",
+ "mac/foundation_util_unittest.mm",
+ "mac/objc_property_releaser_unittest.mm",
+ "mac/scoped_nsobject_unittest.mm",
+ "sys_string_conversions_mac_unittest.mm",
+ ]
+ set_sources_assignment_filter(sources_assignment_filter)
+
+ # TODO(GYP): dep on copy_test_data_ios action.
+ }
+
+ if (is_linux) {
+ sources -= [ "file_version_info_unittest.cc" ]
+ sources += [ "nix/xdg_util_unittest.cc" ]
+ defines = [ "USE_SYMBOLIZE" ]
+ configs += [ "//build/config/linux:glib" ]
+ }
+
+ if (!is_linux || use_ozone) {
+ sources -= [ "message_loop/message_pump_glib_unittest.cc" ]
+ }
+
+ if (is_posix || is_ios) {
+ sources += [ "message_loop/message_pump_libevent_unittest.cc" ]
+ deps += [ "//third_party/libevent" ]
+ }
+
+ if (is_android) {
+ set_sources_assignment_filter([])
+ sources += [ "debug/proc_maps_linux_unittest.cc" ]
+ set_sources_assignment_filter(sources_assignment_filter)
+ }
+}
+
+if (is_android) {
+ generate_jni("base_jni_headers") {
+ sources = [
+ "android/java/src/org/chromium/base/ApplicationStatus.java",
+ "android/java/src/org/chromium/base/BuildInfo.java",
+ "android/java/src/org/chromium/base/CommandLine.java",
+ "android/java/src/org/chromium/base/ContentUriUtils.java",
+ "android/java/src/org/chromium/base/CpuFeatures.java",
+ "android/java/src/org/chromium/base/EventLog.java",
+ "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java",
+ "android/java/src/org/chromium/base/library_loader/LibraryLoader.java",
+ "android/java/src/org/chromium/base/MemoryPressureListener.java",
+ "android/java/src/org/chromium/base/JavaHandlerThread.java",
+ "android/java/src/org/chromium/base/PathService.java",
+ "android/java/src/org/chromium/base/PathUtils.java",
+ "android/java/src/org/chromium/base/PowerMonitor.java",
+ "android/java/src/org/chromium/base/SystemMessageHandler.java",
+ "android/java/src/org/chromium/base/SysUtils.java",
+ "android/java/src/org/chromium/base/ThreadUtils.java",
+ "android/java/src/org/chromium/base/TraceEvent.java",
+ ]
+ jni_package = "base"
+ }
+
+ java_cpp_template("base_java_application_state") {
+ sources = [
+ "android/java/src/org/chromium/base/ApplicationState.template",
+ ]
+ source_prereqs = [
+ "android/application_state_list.h"
+ ]
+
+ package_name = "org/chromium/base"
+ }
+
+ java_cpp_template("base_java_memory_pressure_level_list") {
+ sources = [
+ "android/java/src/org/chromium/base/MemoryPressureLevelList.template",
+ ]
+ source_prereqs = [
+ "memory/memory_pressure_level_list.h"
+ ]
+
+ package_name = "org/chromium/base"
+ }
+
+ java_cpp_template("base_native_libraries_gen") {
+ sources = [
+ "android/java/templates/NativeLibraries.template",
+ ]
+ source_prereqs = [
+ "android/java/templates/native_libraries_array.h",
+ ]
+
+ package_name = "org/chromium/base/library_loader"
+ include_path = "android/java/templates"
+ }
+
+
+}
diff --git a/chromium/base/DEPS b/chromium/base/DEPS
index e1545aee519..2407baaa53c 100644
--- a/chromium/base/DEPS
+++ b/chromium/base/DEPS
@@ -1,15 +1,18 @@
include_rules = [
"+jni",
- "+native_client/src/untrusted/irt/irt.h",
"+third_party/ashmem",
"+third_party/apple_apsl",
"+third_party/libevent",
"+third_party/dmg_fp",
- "+third_party/GTM",
+ "+third_party/google_toolbox_for_mac/src",
"+third_party/mach_override",
"+third_party/modp_b64",
"+third_party/tcmalloc",
+ # These are implicitly brought in from the root, and we don't want them.
+ "-ipc",
+ "-url",
+
# ICU dependendencies must be separate from the rest of base.
"-i18n",
]
diff --git a/chromium/base/PRESUBMIT.py b/chromium/base/PRESUBMIT.py
index 7137c5a0101..1a1cce75def 100644
--- a/chromium/base/PRESUBMIT.py
+++ b/chromium/base/PRESUBMIT.py
@@ -48,9 +48,11 @@ def CheckChangeOnCommit(input_api, output_api):
return results
-def GetPreferredTrySlaves():
- return [
- 'linux_rel:sync_integration_tests',
- 'mac_rel:sync_integration_tests',
- 'win_rel:sync_integration_tests',
- ]
+def GetPreferredTryMasters(project, change):
+ return {
+ 'tryserver.chromium': {
+ 'linux_chromium_rel': set(['defaulttests']),
+ 'mac_chromium_rel': set(['defaulttests']),
+ 'win_chromium_rel': set(['defaulttests']),
+ }
+ }
diff --git a/chromium/base/allocator/BUILD.gn b/chromium/base/allocator/BUILD.gn
new file mode 100644
index 00000000000..d8e3b275a0f
--- /dev/null
+++ b/chromium/base/allocator/BUILD.gn
@@ -0,0 +1,248 @@
+# 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.
+
+import("//build/config/allocator.gni")
+
+# Only executables and not libraries should depend on the allocator target;
+# only the application (the final executable) knows what allocator makes sense.
+# This "allocator" meta-target will forward to the default allocator according
+# to the build settings.
+group("allocator") {
+ if (use_allocator == "tcmalloc") {
+ deps = [ ":tcmalloc" ]
+ }
+}
+
+# This config and libc modification are only used on Windows.
+if (is_win) {
+ import("//build/config/win/visual_studio_version.gni")
+
+ config("nocmt") {
+ ldflags = [
+ "/NODEFAULTLIB:libcmt",
+ "/NODEFAULTLIB:libcmtd",
+ ]
+ libs = [ rebase_path("$target_gen_dir/allocator/libcmt.lib") ]
+ }
+
+ action("prep_libc") {
+ script = "prep_libc.py"
+ outputs = [ "$target_gen_dir/allocator/libcmt.lib" ]
+ args = [
+ visual_studio_path + "/vc/lib",
+ rebase_path("$target_gen_dir/allocator"),
+ cpu_arch,
+ ]
+ }
+}
+
+if (!is_android) {
+ # tcmalloc currently won't compile on Android.
+ source_set("tcmalloc") {
+ tcmalloc_dir = "//third_party/tcmalloc/chromium"
+
+ sources = [
+ # Generated for our configuration from tcmalloc"s build
+ # and checked in.
+ "$tcmalloc_dir/src/config.h",
+ "$tcmalloc_dir/src/config_android.h",
+ "$tcmalloc_dir/src/config_linux.h",
+ "$tcmalloc_dir/src/config_win.h",
+
+ # tcmalloc native and forked files.
+ "$tcmalloc_dir/src/base/abort.cc",
+ "$tcmalloc_dir/src/base/abort.h",
+ "$tcmalloc_dir/src/base/arm_instruction_set_select.h",
+ # We don't list dynamic_annotations.c since its copy is already
+ # present in the dynamic_annotations target.
+ "$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/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/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/synchronization_profiling.h",
+ "$tcmalloc_dir/src/base/sysinfo.cc",
+ "$tcmalloc_dir/src/base/sysinfo.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",
+ # #included by debugallocation_shim.cc
+ #"$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/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/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/maybe_threads.cc",
+ "$tcmalloc_dir/src/maybe_threads.h",
+ "$tcmalloc_dir/src/memory_region_map.cc",
+ "$tcmalloc_dir/src/memory_region_map.h",
+ "$tcmalloc_dir/src/page_heap.cc",
+ "$tcmalloc_dir/src/page_heap.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/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",
+ # #included by debugallocation_shim.cc
+ #"$tcmalloc_dir/src/tcmalloc.cc",
+ "$tcmalloc_dir/src/thread_cache.cc",
+ "$tcmalloc_dir/src/thread_cache.h",
+ "$tcmalloc_dir/src/windows/port.cc",
+ "$tcmalloc_dir/src/windows/port.h",
+
+ "allocator_shim.cc",
+ "allocator_shim.h",
+ "debugallocation_shim.cc",
+
+ # These are both #included by allocator_shim for maximal linking.
+ #"generic_allocators.cc",
+ #"win_allocator.cc",
+ ]
+
+ # Disable the heap checker in tcmalloc.
+ defines = [ "NO_HEAP_CHECK" ]
+
+ include_dirs = [
+ ".",
+ "$tcmalloc_dir/src/base",
+ "$tcmalloc_dir/src",
+ ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+
+ deps = []
+
+ if (is_win) {
+ 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",
+
+ # cpuprofiler
+ "$tcmalloc_dir/src/base/thread_lister.c",
+ "$tcmalloc_dir/src/base/thread_lister.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",
+ ]
+ defines += [ "PERFTOOLS_DLL_DECL=" ]
+
+ configs -= [
+ # Tcmalloc defines this itself, and we don't want duplicate definition
+ # warnings.
+ "//build/config/win:nominmax",
+ ]
+
+ direct_dependent_configs = [ ":nocmt" ]
+
+ deps += [
+ ":prep_libc",
+ ]
+ }
+
+ if (is_linux || is_android) {
+ 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",
+ ]
+
+ # 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",
+ ]
+
+ configs -= [ "//build/config/gcc:symbol_visibility_hidden" ]
+
+ 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",
+ ]
+ }
+
+ # Make sure the allocation library is optimized as much as possible when
+ # we"re in release mode.
+ if (!is_debug) {
+ configs -= [ "//build/config/compiler:optimize" ]
+ configs += [ "//build/config/compiler:optimize_max" ]
+ }
+
+ deps += [
+ "//base/third_party/dynamic_annotations",
+ ]
+
+ if (is_win) {
+ ldflags = [ "/ignore:4006:4221" ]
+ }
+ }
+} # !is_android
+
+source_set("allocator_extension_thunks") {
+ visibility = "//base/*"
+ sources = [
+ "allocator_extension_thunks.cc",
+ "allocator_extension_thunks.h",
+ ]
+}
diff --git a/chromium/base/allocator/allocator.gyp b/chromium/base/allocator/allocator.gyp
index 4841f58c9a4..2de3aca450f 100644
--- a/chromium/base/allocator/allocator.gyp
+++ b/chromium/base/allocator/allocator.gyp
@@ -4,7 +4,6 @@
{
'variables': {
- 'jemalloc_dir': '../../third_party/jemalloc/chromium',
'tcmalloc_dir': '../../third_party/tcmalloc/chromium',
'use_vtable_verify%': 0,
},
@@ -198,13 +197,6 @@
'<(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',
@@ -358,7 +350,6 @@
'libcmt',
],
'include_dirs': [
- '<(jemalloc_dir)',
'<(tcmalloc_dir)/src/windows',
],
'sources!': [
@@ -376,7 +367,10 @@
# included by allocator_shim.cc
'debugallocation_shim.cc',
-
+ ],
+ }],
+ ['OS=="win" or profiling!=1', {
+ 'sources!': [
# cpuprofiler
'<(tcmalloc_dir)/src/base/thread_lister.c',
'<(tcmalloc_dir)/src/base/thread_lister.h',
@@ -395,15 +389,6 @@
# 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:
@@ -426,11 +411,6 @@
'-Wl,-u_ZN15HeapLeakChecker12IgnoreObjectEPKv,-u_ZN15HeapLeakChecker14UnIgnoreObjectEPKv',
]},
}],
- # Need to distinguish a non-SDK build for Android WebView
- # due to differences in C include files.
- ['OS=="android" and android_webview_build==1', {
- 'defines': ['ANDROID_NON_SDK_BUILD'],
- }],
[ 'use_vtable_verify==1', {
'cflags': [
'-fvtable-verify=preinit',
@@ -513,7 +493,7 @@
'../..',
],
'sources': [
- 'allocator_unittests.cc',
+ 'allocator_unittest.cc',
'../profiler/alternate_timer.cc',
'../profiler/alternate_timer.h',
],
@@ -616,7 +596,7 @@
'sources': [
'type_profiler_control.cc',
'type_profiler_control.h',
- 'type_profiler_unittests.cc',
+ 'type_profiler_unittest.cc',
],
},
{
@@ -638,7 +618,7 @@
'../..',
],
'sources': [
- 'type_profiler_map_unittests.cc',
+ 'type_profiler_map_unittest.cc',
'<(tcmalloc_dir)/src/gperftools/type_profiler_map.h',
'<(tcmalloc_dir)/src/type_profiler_map.cc',
],
diff --git a/chromium/base/allocator/allocator_shim.cc b/chromium/base/allocator/allocator_shim.cc
index 9b3b50c4ee4..c0de36e6de2 100644
--- a/chromium/base/allocator/allocator_shim.cc
+++ b/chromium/base/allocator/allocator_shim.cc
@@ -8,7 +8,6 @@
#include "base/allocator/allocator_extension_thunks.h"
#include "base/profiler/alternate_timer.h"
#include "base/sysinfo.h"
-#include "jemalloc.h"
// This shim make it possible to use different allocators via an environment
// variable set before running the program. This may reduce the
@@ -33,8 +32,7 @@ static int new_mode = 0;
typedef enum {
TCMALLOC, // TCMalloc is the default allocator.
- JEMALLOC, // JEMalloc.
- WINHEAP, // Windows Heap (standard Windows allocator).
+ WINHEAP, // Windows Heap (standard Windows allocator).
WINLFH, // Windows LFH Heap.
} Allocator;
@@ -43,8 +41,8 @@ typedef enum {
// See SetupSubprocessAllocator() to specify a default secondary (subprocess)
// allocator.
// TODO(jar): Switch to using TCMALLOC for the renderer as well.
-#if (defined(ADDRESS_SANITIZER) && defined(OS_WIN))
-// The Windows implementation of Asan requires the use of "WINHEAP".
+#if defined(SYZYASAN)
+// SyzyASan requires the use of "WINHEAP".
static Allocator allocator = WINHEAP;
#else
static Allocator allocator = TCMALLOC;
@@ -61,16 +59,6 @@ static const char secondary_name[] = "CHROME_ALLOCATOR_2";
#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);
-}
-
// 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) {
@@ -118,9 +106,6 @@ void* malloc(size_t size) __THROW {
void* ptr;
for (;;) {
switch (allocator) {
- case JEMALLOC:
- ptr = je_malloc(size);
- break;
case WINHEAP:
case WINLFH:
ptr = win_heap_malloc(size);
@@ -141,9 +126,6 @@ void* malloc(size_t size) __THROW {
void free(void* p) __THROW {
switch (allocator) {
- case JEMALLOC:
- je_free(p);
- return;
case WINHEAP:
case WINLFH:
win_heap_free(p);
@@ -164,9 +146,6 @@ void* realloc(void* ptr, size_t size) __THROW {
void* new_ptr;
for (;;) {
switch (allocator) {
- case JEMALLOC:
- new_ptr = je_realloc(ptr, size);
- break;
case WINHEAP:
case WINLFH:
new_ptr = win_heap_realloc(ptr, size);
@@ -191,9 +170,6 @@ void* realloc(void* ptr, size_t size) __THROW {
// TODO(mbelshe): Implement this for other allocators.
void malloc_stats(void) __THROW {
switch (allocator) {
- case JEMALLOC:
- // No stats.
- return;
case WINHEAP:
case WINLFH:
// No stats.
@@ -208,8 +184,6 @@ void malloc_stats(void) __THROW {
extern "C" size_t _msize(void* p) {
switch (allocator) {
- case JEMALLOC:
- return je_msize(p);
case WINHEAP:
case WINLFH:
return win_heap_msize(p);
@@ -226,7 +200,6 @@ extern "C" intptr_t _get_heap_handle() {
static bool get_allocator_waste_size_thunk(size_t* size) {
switch (allocator) {
- case JEMALLOC:
case WINHEAP:
case WINLFH:
// TODO(alexeif): Implement for allocators other than tcmalloc.
@@ -255,14 +228,12 @@ static void release_free_memory_thunk() {
// The CRT heap initialization stub.
extern "C" int _heap_init() {
-// Don't use the environment variable if ADDRESS_SANITIZER is defined on
-// Windows, as the implementation requires Winheap to be the allocator.
-#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
+// Don't use the environment variable if SYZYASAN is defined, as the
+// implementation requires Winheap to be the allocator.
+#if !defined(SYZYASAN)
const char* environment_value = GetenvBeforeMain(primary_name);
if (environment_value) {
- if (!stricmp(environment_value, "jemalloc"))
- allocator = JEMALLOC;
- else if (!stricmp(environment_value, "winheap"))
+ if (!stricmp(environment_value, "winheap"))
allocator = WINHEAP;
else if (!stricmp(environment_value, "winlfh"))
allocator = WINLFH;
@@ -272,8 +243,6 @@ extern "C" int _heap_init() {
#endif
switch (allocator) {
- case JEMALLOC:
- return je_malloc_init_hard() ? 0 : 1;
case WINHEAP:
return win_heap_init(false) ? 1 : 0;
case WINLFH:
@@ -331,9 +300,6 @@ void* _aligned_malloc(size_t size, size_t alignment) {
void* ptr;
for (;;) {
switch (allocator) {
- case JEMALLOC:
- ptr = je_memalign(alignment, size);
- break;
case WINHEAP:
case WINLFH:
ptr = win_heap_memalign(alignment, size);
@@ -357,13 +323,10 @@ void* _aligned_malloc(size_t size, size_t alignment) {
}
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.
+ // TCMalloc returns 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.
switch (allocator) {
- case JEMALLOC:
- je_free(p);
- return;
case WINHEAP:
case WINLFH:
win_heap_memalign_free(p);
@@ -393,9 +356,9 @@ void SetupSubprocessAllocator() {
buffer[sizeof(buffer) - 1] = '\0';
if (secondary_length || !primary_length) {
- // Don't use the environment variable if ADDRESS_SANITIZER is defined on
- // Windows, as the implementation require Winheap to be the allocator.
-#if !(defined(ADDRESS_SANITIZER) && defined(OS_WIN))
+// Don't use the environment variable if SYZYASAN is defined, as the
+// implementation require Winheap to be the allocator.
+#if !defined(SYZYASAN)
const char* secondary_value = secondary_length ? buffer : "TCMALLOC";
// Force renderer (or other subprocesses) to use secondary_value.
#else
diff --git a/chromium/base/allocator/allocator_unittests.cc b/chromium/base/allocator/allocator_unittest.cc
index cf8b74d7f4e..eb20b38ab61 100644
--- a/chromium/base/allocator/allocator_unittests.cc
+++ b/chromium/base/allocator/allocator_unittest.cc
@@ -1,10 +1,11 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. 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"
@@ -81,8 +82,6 @@ static int NextSize(int size) {
}
}
-#define GG_ULONGLONG(x) static_cast<uint64>(x)
-
template <class AtomicType>
static void TestAtomicIncrement() {
// For now, we just test single threaded execution
@@ -164,7 +163,7 @@ static void TestCompareAndSwap() {
// 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) <<
+ const AtomicType k_test_val = (static_cast<uint64_t>(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5);
@@ -187,7 +186,7 @@ static void TestAtomicExchange() {
// 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) <<
+ const AtomicType k_test_val = (static_cast<uint64_t>(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val);
@@ -205,7 +204,7 @@ 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 test_val = static_cast<uint64_t>(1) << (NUM_BITS(AtomicType) / 2);
AtomicType value = test_val - 1;
AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
EXPECT_EQ(test_val, value);
diff --git a/chromium/base/allocator/prep_libc.py b/chromium/base/allocator/prep_libc.py
index ba25cea092f..471140cb548 100755
--- a/chromium/base/allocator/prep_libc.py
+++ b/chromium/base/allocator/prep_libc.py
@@ -12,7 +12,7 @@
# VCLibDir is the path where VC is installed, something like:
# C:\Program Files\Microsoft Visual Studio 8\VC\lib
# OutputDir is the directory where the modified libcmt file should be stored.
-# arch is either 'ia32' or 'x64'
+# arch is one of: 'ia32', 'x86' or 'x64'. ia32 and x86 are synonyms.
import os
import shutil
diff --git a/chromium/base/allocator/type_profiler_map_unittests.cc b/chromium/base/allocator/type_profiler_map_unittest.cc
index 5ac5dd0c26f..514ec164e81 100644
--- a/chromium/base/allocator/type_profiler_map_unittests.cc
+++ b/chromium/base/allocator/type_profiler_map_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/base/allocator/type_profiler_unittests.cc b/chromium/base/allocator/type_profiler_unittest.cc
index e8f06eddd61..3d7369c3802 100644
--- a/chromium/base/allocator/type_profiler_unittests.cc
+++ b/chromium/base/allocator/type_profiler_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright 2012 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
diff --git a/chromium/base/android/OWNERS b/chromium/base/android/OWNERS
index 87d5d224978..8e73f048ced 100644
--- a/chromium/base/android/OWNERS
+++ b/chromium/base/android/OWNERS
@@ -1,2 +1,4 @@
bulach@chromium.org
+nyquist@chromium.org
+rmcilroy@chromium.org
yfriedman@chromium.org
diff --git a/chromium/base/android/activity_state_list.h b/chromium/base/android/activity_state_list.h
deleted file mode 100644
index 43c0f803da3..00000000000
--- a/chromium/base/android/activity_state_list.h
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 file intentionally does not have header guards, it's included
-// inside a macro to generate enum values.
-
-#ifndef DEFINE_ACTIVITY_STATE
-#error "DEFINE_ACTIVITY_STATE should be defined before including this file"
-#endif
-DEFINE_ACTIVITY_STATE(CREATED, 1)
-DEFINE_ACTIVITY_STATE(STARTED, 2)
-DEFINE_ACTIVITY_STATE(RESUMED, 3)
-DEFINE_ACTIVITY_STATE(PAUSED, 4)
-DEFINE_ACTIVITY_STATE(STOPPED, 5)
-DEFINE_ACTIVITY_STATE(DESTROYED, 6)
diff --git a/chromium/base/android/activity_status.cc b/chromium/base/android/activity_status.cc
deleted file mode 100644
index 4d0be32ef93..00000000000
--- a/chromium/base/android/activity_status.cc
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright 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.
-
-#include "base/android/activity_status.h"
-
-#include <jni.h>
-
-#include "base/memory/singleton.h"
-#include "jni/ActivityStatus_jni.h"
-
-namespace base {
-namespace android {
-
-ActivityStatus::Listener::Listener(
- const ActivityStatus::StateChangeCallback& callback)
- : callback_(callback) {
- ActivityStatus::GetInstance()->RegisterListener(this);
-}
-
-ActivityStatus::Listener::~Listener() {
- ActivityStatus::GetInstance()->UnregisterListener(this);
-}
-
-void ActivityStatus::Listener::Notify(ActivityState state) {
- callback_.Run(state);
-}
-
-// static
-ActivityStatus* ActivityStatus::GetInstance() {
- return Singleton<ActivityStatus,
- LeakySingletonTraits<ActivityStatus> >::get();
-}
-
-static void OnActivityStateChange(JNIEnv* env, jclass clazz, int new_state) {
- ActivityStatus* activity_status = ActivityStatus::GetInstance();
- ActivityState activity_state = static_cast<ActivityState>(new_state);
- activity_status->OnActivityStateChange(activity_state);
-}
-
-bool ActivityStatus::RegisterBindings(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
-ActivityStatus::ActivityStatus()
- : observers_(new ObserverListThreadSafe<Listener>()) {
- Java_ActivityStatus_registerThreadSafeNativeStateListener(
- base::android::AttachCurrentThread());
-}
-
-ActivityStatus::~ActivityStatus() {}
-
-void ActivityStatus::RegisterListener(Listener* listener) {
- observers_->AddObserver(listener);
-}
-
-void ActivityStatus::UnregisterListener(Listener* listener) {
- observers_->RemoveObserver(listener);
-}
-
-void ActivityStatus::OnActivityStateChange(ActivityState new_state) {
- observers_->Notify(&ActivityStatus::Listener::Notify, new_state);
-}
-
-} // namespace android
-} // namespace base
diff --git a/chromium/base/android/activity_status.h b/chromium/base/android/activity_status.h
deleted file mode 100644
index 7975a789cd0..00000000000
--- a/chromium/base/android/activity_status.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 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_ANDROID_ACTIVITY_STATUS_H_
-#define BASE_ANDROID_ACTIVITY_STATUS_H_
-
-#include <jni.h>
-
-#include "base/android/jni_android.h"
-#include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/memory/ref_counted.h"
-#include "base/memory/singleton.h"
-#include "base/observer_list_threadsafe.h"
-
-namespace base {
-namespace android {
-
-// Define activity state values like ACTIVITY_STATE_CREATED in a
-// way that ensures they're always the same than their Java counterpart.
-enum ActivityState {
-#define DEFINE_ACTIVITY_STATE(x, y) ACTIVITY_STATE_##x = y,
-#include "base/android/activity_state_list.h"
-#undef DEFINE_ACTIVITY_STATE
-};
-
-// A native helper class to listen to state changes of the current
-// Android Activity. This mirrors org.chromium.base.ActivityStatus.
-// any thread.
-//
-// To start listening, create a new instance, passing a callback to a
-// function that takes an ActivityState parameter. To stop listening,
-// simply delete the listener object. The implementation guarantees
-// that the callback will always be called on the thread that created
-// the listener.
-//
-// Example:
-//
-// void OnActivityStateChange(ActivityState state) {
-// ...
-// }
-//
-// // Start listening.
-// ActivityStatus::Listener* my_listener =
-// new ActivityStatus::Listener(base::Bind(&OnActivityStateChange));
-//
-// ...
-//
-// // Stop listening.
-// delete my_listener
-//
-class BASE_EXPORT ActivityStatus {
- public:
- typedef base::Callback<void(ActivityState)> StateChangeCallback;
-
- class Listener {
- public:
- explicit Listener(const StateChangeCallback& callback);
- ~Listener();
-
- private:
- friend class ActivityStatus;
-
- void Notify(ActivityState state);
-
- StateChangeCallback callback_;
-
- DISALLOW_COPY_AND_ASSIGN(Listener);
- };
-
- // NOTE: The Java ActivityStatus is a singleton too.
- static ActivityStatus* GetInstance();
-
- // Internal use: must be public to be called from base_jni_registrar.cc
- static bool RegisterBindings(JNIEnv* env);
-
- // Internal use only: must be public to be called from JNI and unit tests.
- void OnActivityStateChange(ActivityState new_state);
-
- private:
- friend struct DefaultSingletonTraits<ActivityStatus>;
-
- ActivityStatus();
- ~ActivityStatus();
-
- void RegisterListener(Listener* listener);
- void UnregisterListener(Listener* listener);
-
- scoped_refptr<ObserverListThreadSafe<Listener> > observers_;
-
- DISALLOW_COPY_AND_ASSIGN(ActivityStatus);
-};
-
-} // namespace android
-} // namespace base
-
-#endif // BASE_ANDROID_ACTIVITY_STATUS_H_
diff --git a/chromium/base/android/application_state_list.h b/chromium/base/android/application_state_list.h
new file mode 100644
index 00000000000..cbc833e54e5
--- /dev/null
+++ b/chromium/base/android/application_state_list.h
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium 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 intentionally does not have header guards, it's included
+// inside a macro to generate enum values.
+
+// Note that these states represent the most visible Activity state.
+// If there are activities with states paused and stopped, only
+// HAS_PAUSED_ACTIVITIES should be returned.
+#ifndef DEFINE_APPLICATION_STATE
+#error "DEFINE_APPLICATION_STATE should be defined before including this file"
+#endif
+DEFINE_APPLICATION_STATE(HAS_RUNNING_ACTIVITIES, 1)
+DEFINE_APPLICATION_STATE(HAS_PAUSED_ACTIVITIES, 2)
+DEFINE_APPLICATION_STATE(HAS_STOPPED_ACTIVITIES, 3)
+DEFINE_APPLICATION_STATE(HAS_DESTROYED_ACTIVITIES, 4)
diff --git a/chromium/base/android/application_status_listener.cc b/chromium/base/android/application_status_listener.cc
new file mode 100644
index 00000000000..f48707282f5
--- /dev/null
+++ b/chromium/base/android/application_status_listener.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All 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/application_status_listener.h"
+
+#include <jni.h>
+
+#include "base/lazy_instance.h"
+#include "base/observer_list_threadsafe.h"
+#include "jni/ApplicationStatus_jni.h"
+
+namespace {
+struct LeakyLazyObserverListTraits :
+ base::internal::LeakyLazyInstanceTraits<
+ ObserverListThreadSafe<base::android::ApplicationStatusListener> > {
+ static ObserverListThreadSafe<base::android::ApplicationStatusListener>*
+ New(void* instance) {
+ ObserverListThreadSafe<base::android::ApplicationStatusListener>* ret =
+ base::internal::LeakyLazyInstanceTraits<ObserverListThreadSafe<
+ base::android::ApplicationStatusListener> >::New(instance);
+ // Leaky.
+ ret->AddRef();
+ return ret;
+ }
+};
+
+base::LazyInstance<ObserverListThreadSafe<
+ base::android::ApplicationStatusListener>,
+ LeakyLazyObserverListTraits> g_observers = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace base {
+namespace android {
+
+ApplicationStatusListener::ApplicationStatusListener(
+ const ApplicationStatusListener::ApplicationStateChangeCallback& callback)
+ : callback_(callback) {
+ DCHECK(!callback_.is_null());
+ g_observers.Get().AddObserver(this);
+
+ Java_ApplicationStatus_registerThreadSafeNativeApplicationStateListener(
+ base::android::AttachCurrentThread());
+}
+
+ApplicationStatusListener::~ApplicationStatusListener() {
+ g_observers.Get().RemoveObserver(this);
+}
+
+void ApplicationStatusListener::Notify(ApplicationState state) {
+ callback_.Run(state);
+}
+
+// static
+bool ApplicationStatusListener::RegisterBindings(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+// static
+void ApplicationStatusListener::NotifyApplicationStateChange(
+ ApplicationState state) {
+ g_observers.Get().Notify(&ApplicationStatusListener::Notify, state);
+}
+
+static void OnApplicationStateChange(JNIEnv* env,
+ jclass clazz,
+ jint new_state) {
+ ApplicationState application_state = static_cast<ApplicationState>(new_state);
+ ApplicationStatusListener::NotifyApplicationStateChange(application_state);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/application_status_listener.h b/chromium/base/android/application_status_listener.h
new file mode 100644
index 00000000000..ef98985f6bc
--- /dev/null
+++ b/chromium/base/android/application_status_listener.h
@@ -0,0 +1,79 @@
+// Copyright 2014 The Chromium Authors. All 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_APPLICATION_STATUS_LISTENER_H_
+#define BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
+
+#include <jni.h>
+
+#include "base/android/jni_android.h"
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/singleton.h"
+#include "base/observer_list_threadsafe.h"
+
+namespace base {
+namespace android {
+
+// Define application state values like APPLICATION_STATE_VISIBLE in a
+// way that ensures they're always the same than their Java counterpart.
+enum ApplicationState {
+#define DEFINE_APPLICATION_STATE(x, y) APPLICATION_STATE_##x = y,
+#include "base/android/application_state_list.h"
+#undef DEFINE_APPLICATION_STATE
+};
+
+// A native helper class to listen to state changes of the Android
+// Application. This mirrors org.chromium.base.ApplicationStatus.
+// any thread.
+//
+// To start listening, create a new instance, passing a callback to a
+// function that takes an ApplicationState parameter. To stop listening,
+// simply delete the listener object. The implementation guarantees
+// that the callback will always be called on the thread that created
+// the listener.
+//
+// Example:
+//
+// void OnApplicationStateChange(ApplicationState state) {
+// ...
+// }
+//
+// // Start listening.
+// ApplicationStatusListener* my_listener =
+// new ApplicationStatusListener(
+// base::Bind(&OnApplicationStateChange));
+//
+// ...
+//
+// // Stop listening.
+// delete my_listener
+//
+class BASE_EXPORT ApplicationStatusListener {
+ public:
+ typedef base::Callback<void(ApplicationState)> ApplicationStateChangeCallback;
+
+ explicit ApplicationStatusListener(
+ const ApplicationStateChangeCallback& callback);
+ ~ApplicationStatusListener();
+
+ // Internal use: must be public to be called from base_jni_registrar.cc
+ static bool RegisterBindings(JNIEnv* env);
+
+ // Internal use only: must be public to be called from JNI and unit tests.
+ static void NotifyApplicationStateChange(ApplicationState state);
+
+ private:
+ void Notify(ApplicationState state);
+
+ ApplicationStateChangeCallback callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(ApplicationStatusListener);
+};
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_APPLICATION_STATUS_LISTENER_H_
diff --git a/chromium/base/android/activity_status_unittest.cc b/chromium/base/android/application_status_listener_unittest.cc
index 3eb0d10f736..1049628f5a3 100644
--- a/chromium/base/android/activity_status_unittest.cc
+++ b/chromium/base/android/application_status_listener_unittest.cc
@@ -1,8 +1,8 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All 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/activity_status.h"
+#include "base/android/application_status_listener.h"
#include "base/bind.h"
#include "base/callback_forward.h"
#include "base/logging.h"
@@ -20,11 +20,12 @@ namespace {
using base::android::ScopedJavaLocalRef;
-// An invalid ActivityState value.
-const ActivityState kInvalidActivityState = static_cast<ActivityState>(100);
+// An invalid ApplicationState value.
+const ApplicationState kInvalidApplicationState =
+ static_cast<ApplicationState>(100);
// Used to generate a callback that stores the new state at a given location.
-void StoreStateTo(ActivityState* target, ActivityState state) {
+void StoreStateTo(ApplicationState* target, ApplicationState state) {
*target = state;
}
@@ -39,10 +40,9 @@ void RunTasksUntilIdle() {
class MultiThreadedTest {
public:
MultiThreadedTest()
- : activity_status_(ActivityStatus::GetInstance()),
- state_(kInvalidActivityState),
+ : state_(kInvalidApplicationState),
event_(false, false),
- thread_("ActivityStatusTest thread"),
+ thread_("ApplicationStatusTest thread"),
main_() {
}
@@ -58,14 +58,16 @@ class MultiThreadedTest {
event_.Wait();
// Change state, then wait for the thread to modify state.
- activity_status_->OnActivityStateChange(ACTIVITY_STATE_CREATED);
+ ApplicationStatusListener::NotifyApplicationStateChange(
+ APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
event_.Wait();
- EXPECT_EQ(ACTIVITY_STATE_CREATED, state_);
+ EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, state_);
// Again
- activity_status_->OnActivityStateChange(ACTIVITY_STATE_DESTROYED);
+ ApplicationStatusListener::NotifyApplicationStateChange(
+ APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES);
event_.Wait();
- EXPECT_EQ(ACTIVITY_STATE_DESTROYED, state_);
+ EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, state_);
}
private:
@@ -75,51 +77,51 @@ class MultiThreadedTest {
void RegisterThreadForEvents() {
ExpectOnThread();
- listener_.reset(new ActivityStatus::Listener(base::Bind(
+ listener_.reset(new ApplicationStatusListener(base::Bind(
&MultiThreadedTest::StoreStateAndSignal, base::Unretained(this))));
EXPECT_TRUE(listener_.get());
event_.Signal();
}
- void StoreStateAndSignal(ActivityState state) {
+ void StoreStateAndSignal(ApplicationState state) {
ExpectOnThread();
state_ = state;
event_.Signal();
}
- ActivityStatus* const activity_status_;
- ActivityState state_;
+ ApplicationState state_;
base::WaitableEvent event_;
base::Thread thread_;
base::MessageLoop main_;
- scoped_ptr<ActivityStatus::Listener> listener_;
+ scoped_ptr<ApplicationStatusListener> listener_;
};
} // namespace
-TEST(ActivityStatusTest, SingleThread) {
+TEST(ApplicationStatusListenerTest, SingleThread) {
MessageLoop message_loop;
- ActivityState result = kInvalidActivityState;
+ ApplicationState result = kInvalidApplicationState;
// Create a new listener that stores the new state into |result| on every
// state change.
- ActivityStatus::Listener listener(
+ ApplicationStatusListener listener(
base::Bind(&StoreStateTo, base::Unretained(&result)));
- EXPECT_EQ(kInvalidActivityState, result);
+ EXPECT_EQ(kInvalidApplicationState, result);
- ActivityStatus* const activity_status = ActivityStatus::GetInstance();
- activity_status->OnActivityStateChange(ACTIVITY_STATE_CREATED);
+ ApplicationStatusListener::NotifyApplicationStateChange(
+ APPLICATION_STATE_HAS_RUNNING_ACTIVITIES);
RunTasksUntilIdle();
- EXPECT_EQ(ACTIVITY_STATE_CREATED, result);
+ EXPECT_EQ(APPLICATION_STATE_HAS_RUNNING_ACTIVITIES, result);
- activity_status->OnActivityStateChange(ACTIVITY_STATE_DESTROYED);
+ ApplicationStatusListener::NotifyApplicationStateChange(
+ APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES);
RunTasksUntilIdle();
- EXPECT_EQ(ACTIVITY_STATE_DESTROYED, result);
+ EXPECT_EQ(APPLICATION_STATE_HAS_DESTROYED_ACTIVITIES, result);
}
-TEST(ActivityStatusTest, TwoThreads) {
+TEST(ApplicationStatusListenerTest, TwoThreads) {
MultiThreadedTest test;
test.Run();
}
diff --git a/chromium/base/android/build_info.cc b/chromium/base/android/build_info.cc
index cdde6a90052..a1755afe981 100644
--- a/chromium/base/android/build_info.cc
+++ b/chromium/base/android/build_info.cc
@@ -37,7 +37,9 @@ struct BuildInfoSingletonTraits {
}
static const bool kRegisterAtExit = false;
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
};
BuildInfo::BuildInfo(JNIEnv* env)
@@ -55,6 +57,7 @@ BuildInfo::BuildInfo(JNIEnv* env)
env, GetApplicationContext()))),
package_name_(StrDupJString(Java_BuildInfo_getPackageName(
env, GetApplicationContext()))),
+ build_type_(StrDupJString(Java_BuildInfo_getBuildType(env))),
sdk_int_(Java_BuildInfo_getSdkInt(env)),
java_exception_info_(NULL) {
}
diff --git a/chromium/base/android/build_info.h b/chromium/base/android/build_info.h
index a5f44c2e0ac..03dadd99df1 100644
--- a/chromium/base/android/build_info.h
+++ b/chromium/base/android/build_info.h
@@ -72,6 +72,10 @@ class BASE_EXPORT BuildInfo {
return package_name_;
}
+ const char* build_type() const {
+ return build_type_;
+ }
+
int sdk_int() const {
return sdk_int_;
}
@@ -102,6 +106,7 @@ class BASE_EXPORT BuildInfo {
const char* const package_version_name_;
const char* const package_label_;
const char* const package_name_;
+ const char* const build_type_;
const int sdk_int_;
// This is set via set_java_exception_info, not at constructor time.
const char* java_exception_info_;
diff --git a/chromium/base/android/content_uri_utils.cc b/chromium/base/android/content_uri_utils.cc
index 64d6ad24226..0e0c0ea6bb1 100644
--- a/chromium/base/android/content_uri_utils.cc
+++ b/chromium/base/android/content_uri_utils.cc
@@ -6,7 +6,6 @@
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
-#include "base/platform_file.h"
#include "jni/ContentUriUtils_jni.h"
using base::android::ConvertUTF8ToJavaString;
@@ -25,15 +24,15 @@ bool ContentUriExists(const FilePath& content_uri) {
env, base::android::GetApplicationContext(), j_uri.obj());
}
-int OpenContentUriForRead(const FilePath& content_uri) {
+File OpenContentUriForRead(const FilePath& content_uri) {
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jstring> j_uri =
ConvertUTF8ToJavaString(env, content_uri.value());
jint fd = Java_ContentUriUtils_openContentUriForRead(
env, base::android::GetApplicationContext(), j_uri.obj());
if (fd < 0)
- return base::kInvalidPlatformFileValue;
- return fd;
+ return File();
+ return File(fd);
}
} // namespace base
diff --git a/chromium/base/android/content_uri_utils.h b/chromium/base/android/content_uri_utils.h
index ec820efbd26..827ec92fa39 100644
--- a/chromium/base/android/content_uri_utils.h
+++ b/chromium/base/android/content_uri_utils.h
@@ -9,6 +9,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
namespace base {
@@ -17,7 +18,7 @@ bool RegisterContentUriUtils(JNIEnv* env);
// Opens a content uri for read and returns the file descriptor to the caller.
// Returns -1 if the uri is invalid.
-BASE_EXPORT int OpenContentUriForRead(const FilePath& content_uri);
+BASE_EXPORT File OpenContentUriForRead(const FilePath& content_uri);
// Check whether a content uri exists.
BASE_EXPORT bool ContentUriExists(const FilePath& content_uri);
diff --git a/chromium/base/android/context_types.cc b/chromium/base/android/context_types.cc
deleted file mode 100644
index 084b1365ce2..00000000000
--- a/chromium/base/android/context_types.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-// 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.
-
-#include "base/android/context_types.h"
-
-#include "base/android/jni_android.h"
-#include "base/android/scoped_java_ref.h"
-#include "base/files/file_path.h"
-#include "jni/ContextTypes_jni.h"
-
-namespace base {
-namespace android {
-
-bool IsRunningInWebapp() {
- JNIEnv* env = AttachCurrentThread();
- return static_cast<bool>(
- Java_ContextTypes_isRunningInWebapp(env, GetApplicationContext()));
-}
-
-bool RegisterContextTypes(JNIEnv* env) {
- return RegisterNativesImpl(env);
-}
-
-} // namespace android
-} // namespace base
diff --git a/chromium/base/android/context_types.h b/chromium/base/android/context_types.h
deleted file mode 100644
index a132167199e..00000000000
--- a/chromium/base/android/context_types.h
+++ /dev/null
@@ -1,22 +0,0 @@
-// 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_ANDROID_CONTEXT_TYPES_H_
-#define BASE_ANDROID_CONTEXT_TYPES_H_
-
-#include <jni.h>
-
-#include "base/base_export.h"
-
-namespace base {
-namespace android {
-
-BASE_EXPORT bool IsRunningInWebapp();
-
-bool RegisterContextTypes(JNIEnv* env);
-
-} // namespace android
-} // namespace base
-
-#endif // BASE_ANDROID_CONTEXT_TYPES_H_
diff --git a/chromium/base/android/event_log.cc b/chromium/base/android/event_log.cc
new file mode 100644
index 00000000000..a4b1dd139f2
--- /dev/null
+++ b/chromium/base/android/event_log.cc
@@ -0,0 +1,20 @@
+// Copyright 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.
+
+#include "base/android/event_log.h"
+#include "jni/EventLog_jni.h"
+
+namespace base {
+namespace android {
+
+void EventLogWriteInt(int tag, int value) {
+ Java_EventLog_writeEvent(AttachCurrentThread(), tag, value);
+}
+
+bool RegisterEventLog(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/event_log.h b/chromium/base/android/event_log.h
new file mode 100644
index 00000000000..dad4e4cfbc9
--- /dev/null
+++ b/chromium/base/android/event_log.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All 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_EVENT_LOG_H_
+#define BASE_ANDROID_EVENT_LOG_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+void BASE_EXPORT EventLogWriteInt(int tag, int value);
+
+bool RegisterEventLog(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_EVENT_LOG_H_
diff --git a/chromium/base/android/jni_generator/jni_generator.gyp b/chromium/base/android/jni_generator/jni_generator.gyp
index 4bad0dc4832..2ea36b0b389 100644
--- a/chromium/base/android/jni_generator/jni_generator.gyp
+++ b/chromium/base/android/jni_generator/jni_generator.gyp
@@ -56,9 +56,6 @@
'jni_sample_header',
'jni_sample_java',
],
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/example',
- ],
'sources': [
'sample_for_tests.cc',
],
diff --git a/chromium/base/android/library_loader/library_loader_hooks.cc b/chromium/base/android/library_loader/library_loader_hooks.cc
new file mode 100644
index 00000000000..79470108eb3
--- /dev/null
+++ b/chromium/base/android/library_loader/library_loader_hooks.cc
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All 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/library_loader/library_loader_hooks.h"
+
+#include "base/android/jni_string.h"
+#include "base/at_exit.h"
+#include "base/metrics/histogram.h"
+#include "jni/LibraryLoader_jni.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+base::AtExitManager* g_at_exit_manager = NULL;
+const char* g_library_version_number = "";
+LibraryLoadedHook* g_registration_callback = NULL;
+
+} // namespace
+
+void SetLibraryLoadedHook(LibraryLoadedHook* func) {
+ g_registration_callback = func;
+}
+
+static jboolean LibraryLoaded(JNIEnv* env, jclass clazz,
+ jobjectArray init_command_line) {
+ if(g_registration_callback == NULL) {
+ return true;
+ }
+ return g_registration_callback(env, clazz, init_command_line);
+}
+
+static void RecordChromiumAndroidLinkerHistogram(
+ JNIEnv* env,
+ jclass clazz,
+ jboolean loaded_at_fixed_address_failed,
+ jboolean is_low_memory_device) {
+ UMA_HISTOGRAM_BOOLEAN("ChromiumAndroidLinker.LoadedAtFixedAddressFailed",
+ loaded_at_fixed_address_failed);
+ UMA_HISTOGRAM_BOOLEAN("ChromiumAndroidLinker.IsLowMemoryDevice",
+ is_low_memory_device);
+}
+
+void LibraryLoaderExitHook() {
+ if (g_at_exit_manager) {
+ delete g_at_exit_manager;
+ g_at_exit_manager = NULL;
+ }
+}
+
+bool RegisterLibraryLoaderEntryHook(JNIEnv* env) {
+ // We need the AtExitManager to be created at the very beginning.
+ g_at_exit_manager = new base::AtExitManager();
+
+ return RegisterNativesImpl(env);
+}
+
+void SetVersionNumber(const char* version_number) {
+ g_library_version_number = strdup(version_number);
+}
+
+jstring GetVersionNumber(JNIEnv* env, jclass clazz) {
+ return ConvertUTF8ToJavaString(env, g_library_version_number).Release();
+}
+
+static void RecordNativeLibraryHack(JNIEnv*, jclass, jboolean usedHack) {
+ UMA_HISTOGRAM_BOOLEAN("LibraryLoader.NativeLibraryHack", usedHack);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/library_loader/library_loader_hooks.h b/chromium/base/android/library_loader/library_loader_hooks.h
new file mode 100644
index 00000000000..06709f6ba9e
--- /dev/null
+++ b/chromium/base/android/library_loader/library_loader_hooks.h
@@ -0,0 +1,51 @@
+// Copyright 2014 The Chromium Authors. All 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_LIBRARY_LOADER_HOOKS_H_
+#define BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Registers the callbacks that allows the entry point of the library to be
+// exposed to the calling java code. This handles only registering the
+// the callbacks needed by the loader. Any application specific JNI bindings
+// should happen once the native library has fully loaded, either in the library
+// loaded hook function or later.
+BASE_EXPORT bool RegisterLibraryLoaderEntryHook(JNIEnv* env);
+
+// Typedef for hook function to be called (indirectly from Java) once the
+// libraries are loaded. The hook function should register the JNI bindings
+// required to start the application. It should return true for success and
+// false for failure.
+// Note: this can't use base::Callback because there is no way of initializing
+// the default callback without using static objects, which we forbid.
+typedef bool LibraryLoadedHook(JNIEnv* env,
+ jclass clazz,
+ jobjectArray init_command_line);
+
+// Set the hook function to be called (from Java) once the libraries are loaded.
+// SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function
+// should register the JNI bindings required to start the application.
+
+BASE_EXPORT void SetLibraryLoadedHook(LibraryLoadedHook* func);
+
+// Pass the version name to the loader. This used to check that the library
+// version matches the version expected by Java before completing JNI
+// registration.
+// Note: argument must remain valid at least until library loading is complete.
+BASE_EXPORT void SetVersionNumber(const char* version_number);
+
+// Call on exit to delete the AtExitManager which OnLibraryLoadedOnUIThread
+// created.
+BASE_EXPORT void LibraryLoaderExitHook();
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_LIBRARY_LOADER_HOOKS_H_
diff --git a/chromium/base/android/linker/DEPS b/chromium/base/android/linker/DEPS
new file mode 100644
index 00000000000..15c3afb8668
--- /dev/null
+++ b/chromium/base/android/linker/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ # This code cannot depend on anything from base/
+ "-base",
+]
diff --git a/chromium/base/android/path_utils_unittest.cc b/chromium/base/android/path_utils_unittest.cc
index c4c12fea133..b6410754eed 100644
--- a/chromium/base/android/path_utils_unittest.cc
+++ b/chromium/base/android/path_utils_unittest.cc
@@ -39,7 +39,8 @@ TEST_F(PathUtilsTest, TestGetNativeLibraryDirectory) {
// the base tests shared object.
FilePath path;
GetNativeLibraryDirectory(&path);
- EXPECT_TRUE(base::PathExists(path.Append(("libbase_unittests.so"))));
+ EXPECT_TRUE(base::PathExists(path.Append(("libbase_unittests.so"))) ||
+ base::PathExists(path.Append(("libbase_unittests.cr.so"))));
}
} // namespace android
diff --git a/chromium/base/android/scoped_java_ref.cc b/chromium/base/android/scoped_java_ref.cc
index 21b466e9584..bb6f5032fe3 100644
--- a/chromium/base/android/scoped_java_ref.cc
+++ b/chromium/base/android/scoped_java_ref.cc
@@ -9,6 +9,24 @@
namespace base {
namespace android {
+namespace {
+
+const int kDefaultLocalFrameCapacity = 16;
+
+} // namespace
+
+ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env) : env_(env) {
+ int failed = env_->PushLocalFrame(kDefaultLocalFrameCapacity);
+ DCHECK(!failed);
+}
+
+ScopedJavaLocalFrame::ScopedJavaLocalFrame(JNIEnv* env, int capacity)
+ : env_(env) {
+ int failed = env_->PushLocalFrame(capacity);
+ DCHECK(!failed);
+}
+
+ScopedJavaLocalFrame::~ScopedJavaLocalFrame() { env_->PopLocalFrame(NULL); }
JavaRef<jobject>::JavaRef() : obj_(NULL) {}
diff --git a/chromium/base/android/scoped_java_ref.h b/chromium/base/android/scoped_java_ref.h
index a5d71e2d23b..7863c0bef3a 100644
--- a/chromium/base/android/scoped_java_ref.h
+++ b/chromium/base/android/scoped_java_ref.h
@@ -14,6 +14,23 @@
namespace base {
namespace android {
+// Creates a new local reference frame, in which at least a given number of
+// local references can be created. Note that local references already created
+// in previous local frames are still valid in the current local frame.
+class BASE_EXPORT ScopedJavaLocalFrame {
+ public:
+ explicit ScopedJavaLocalFrame(JNIEnv* env);
+ ScopedJavaLocalFrame(JNIEnv* env, int capacity);
+ ~ScopedJavaLocalFrame();
+
+ 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_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedJavaLocalFrame);
+};
+
// Forward declare the generic java reference template class.
template<typename T> class JavaRef;
diff --git a/chromium/base/android/sys_utils.h b/chromium/base/android/sys_utils.h
index 9b3152b466c..0841d0a7888 100644
--- a/chromium/base/android/sys_utils.h
+++ b/chromium/base/android/sys_utils.h
@@ -17,7 +17,7 @@ class BASE_EXPORT SysUtils {
// Returns true iff this is a low-end device.
static bool IsLowEndDevice();
- // Return the device's RAM size in kilo-bytes. Used for testing.
+ // Return the device's RAM size in kilo-bytes.
static size_t AmountOfPhysicalMemoryKB();
private:
diff --git a/chromium/base/android/trace_event_binding.cc b/chromium/base/android/trace_event_binding.cc
new file mode 100644
index 00000000000..e261411f08b
--- /dev/null
+++ b/chromium/base/android/trace_event_binding.cc
@@ -0,0 +1,169 @@
+// Copyright 2014 The Chromium Authors. All 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/trace_event_binding.h"
+
+#include <jni.h>
+
+#include <set>
+
+#include "base/debug/trace_event.h"
+#include "base/debug/trace_event_impl.h"
+#include "base/lazy_instance.h"
+#include "jni/TraceEvent_jni.h"
+
+namespace base {
+namespace android {
+
+namespace {
+
+const char kJavaCategory[] = "Java";
+const char kToplevelCategory[] = "toplevel";
+const char kLooperDispatchMessage[] = "Looper.dispatchMessage";
+
+// Boilerplate for safely converting Java data to TRACE_EVENT data.
+class TraceEventDataConverter {
+ public:
+ TraceEventDataConverter(JNIEnv* env,
+ jstring jname,
+ jstring jarg)
+ : env_(env),
+ jname_(jname),
+ jarg_(jarg),
+ name_(env->GetStringUTFChars(jname, NULL)),
+ arg_(jarg ? env->GetStringUTFChars(jarg, NULL) : NULL) {
+ }
+ ~TraceEventDataConverter() {
+ env_->ReleaseStringUTFChars(jname_, name_);
+ if (jarg_)
+ env_->ReleaseStringUTFChars(jarg_, arg_);
+ }
+
+ // Return saves values to pass to TRACE_EVENT macros.
+ const char* name() { return name_; }
+ const char* arg_name() { return arg_ ? "arg" : NULL; }
+ const char* arg() { return arg_; }
+
+ private:
+ JNIEnv* env_;
+ jstring jname_;
+ jstring jarg_;
+ const char* name_;
+ const char* arg_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventDataConverter);
+};
+
+class TraceEnabledObserver : public debug::TraceLog::EnabledStateObserver {
+ public:
+ virtual void OnTraceLogEnabled() OVERRIDE {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::Java_TraceEvent_setEnabled(env, true);
+ }
+ virtual void OnTraceLogDisabled() OVERRIDE {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::Java_TraceEvent_setEnabled(env, false);
+ }
+};
+
+base::LazyInstance<TraceEnabledObserver>::Leaky g_trace_enabled_state_observer_;
+
+} // namespace
+
+static void RegisterEnabledObserver(JNIEnv* env, jclass clazz) {
+ bool enabled = debug::TraceLog::GetInstance()->IsEnabled();
+ base::android::Java_TraceEvent_setEnabled(env, enabled);
+ debug::TraceLog::GetInstance()->AddEnabledStateObserver(
+ g_trace_enabled_state_observer_.Pointer());
+}
+
+static void StartATrace(JNIEnv* env, jclass clazz) {
+ base::debug::TraceLog::GetInstance()->StartATrace();
+}
+
+static void StopATrace(JNIEnv* env, jclass clazz) {
+ base::debug::TraceLog::GetInstance()->StopATrace();
+}
+
+static void Instant(JNIEnv* env, jclass clazz,
+ jstring jname, jstring jarg) {
+ TraceEventDataConverter converter(env, jname, jarg);
+ if (converter.arg()) {
+ TRACE_EVENT_COPY_INSTANT1(kJavaCategory, converter.name(),
+ TRACE_EVENT_SCOPE_THREAD,
+ converter.arg_name(), converter.arg());
+ } else {
+ TRACE_EVENT_COPY_INSTANT0(kJavaCategory, converter.name(),
+ TRACE_EVENT_SCOPE_THREAD);
+ }
+}
+
+static void Begin(JNIEnv* env, jclass clazz,
+ jstring jname, jstring jarg) {
+ TraceEventDataConverter converter(env, jname, jarg);
+ if (converter.arg()) {
+ TRACE_EVENT_COPY_BEGIN1(kJavaCategory, converter.name(),
+ converter.arg_name(), converter.arg());
+ } else {
+ TRACE_EVENT_COPY_BEGIN0(kJavaCategory, converter.name());
+ }
+}
+
+static void End(JNIEnv* env, jclass clazz,
+ jstring jname, jstring jarg) {
+ TraceEventDataConverter converter(env, jname, jarg);
+ if (converter.arg()) {
+ TRACE_EVENT_COPY_END1(kJavaCategory, converter.name(),
+ converter.arg_name(), converter.arg());
+ } else {
+ TRACE_EVENT_COPY_END0(kJavaCategory, converter.name());
+ }
+}
+
+static void BeginToplevel(JNIEnv* env, jclass clazz) {
+ TRACE_EVENT_BEGIN0(kToplevelCategory, kLooperDispatchMessage);
+}
+
+static void EndToplevel(JNIEnv* env, jclass clazz) {
+ TRACE_EVENT_END0(kToplevelCategory, kLooperDispatchMessage);
+}
+
+static void StartAsync(JNIEnv* env, jclass clazz,
+ jstring jname, jlong jid, jstring jarg) {
+ TraceEventDataConverter converter(env, jname, jarg);
+ if (converter.arg()) {
+ TRACE_EVENT_COPY_ASYNC_BEGIN1(kJavaCategory,
+ converter.name(),
+ jid,
+ converter.arg_name(),
+ converter.arg());
+ } else {
+ TRACE_EVENT_COPY_ASYNC_BEGIN0(kJavaCategory,
+ converter.name(),
+ jid);
+ }
+}
+
+static void FinishAsync(JNIEnv* env, jclass clazz,
+ jstring jname, jlong jid, jstring jarg) {
+ TraceEventDataConverter converter(env, jname, jarg);
+ if (converter.arg()) {
+ TRACE_EVENT_COPY_ASYNC_END1(kJavaCategory,
+ converter.name(),
+ jid,
+ converter.arg_name(),
+ converter.arg());
+ } else {
+ TRACE_EVENT_COPY_ASYNC_END0(kJavaCategory,
+ converter.name(),
+ jid);
+ }
+}
+
+bool RegisterTraceEvent(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/trace_event_binding.h b/chromium/base/android/trace_event_binding.h
new file mode 100644
index 00000000000..ed0626620aa
--- /dev/null
+++ b/chromium/base/android/trace_event_binding.h
@@ -0,0 +1,18 @@
+// Copyright 2014 The Chromium Authors. All 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_TRACE_EVENT_H_
+#define BASE_ANDROID_TRACE_EVENT_H_
+
+#include <jni.h>
+
+namespace base {
+namespace android {
+
+extern bool RegisterTraceEvent(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // CONTENT_COMMON_ANDROID_TRACE_EVENT_H_
diff --git a/chromium/base/atomicops.h b/chromium/base/atomicops.h
index 7f03492e849..cb737cd3704 100644
--- a/chromium/base/atomicops.h
+++ b/chromium/base/atomicops.h
@@ -28,7 +28,8 @@
#ifndef BASE_ATOMICOPS_H_
#define BASE_ATOMICOPS_H_
-#include "base/basictypes.h"
+#include <stdint.h>
+
#include "build/build_config.h"
#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS)
@@ -43,7 +44,7 @@
namespace base {
namespace subtle {
-typedef int32 Atomic32;
+typedef int32_t 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.
@@ -133,7 +134,7 @@ Atomic64 Acquire_Load(volatile const Atomic64* ptr);
Atomic64 Release_Load(volatile const Atomic64* ptr);
#endif // ARCH_CPU_64_BITS
-} // namespace base::subtle
+} // namespace subtle
} // namespace base
// Include our platform specific implementation.
@@ -145,8 +146,10 @@ Atomic64 Release_Load(volatile const Atomic64* ptr);
#include "base/atomicops_internals_mac.h"
#elif defined(OS_NACL)
#include "base/atomicops_internals_gcc.h"
-#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
#include "base/atomicops_internals_arm_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64)
+#include "base/atomicops_internals_arm64_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)
diff --git a/chromium/base/atomicops_internals_arm64_gcc.h b/chromium/base/atomicops_internals_arm64_gcc.h
new file mode 100644
index 00000000000..bb6a346eccb
--- /dev/null
+++ b/chromium/base/atomicops_internals_arm64_gcc.h
@@ -0,0 +1,307 @@
+// Copyright 2014 The Chromium 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.
+
+// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of
+// the hand coded assembly without introducing perf regressions.
+// TODO(rmcilroy): Investigate whether we can use acquire / release versions of
+// exclusive load / store assembly instructions and do away with
+// the barriers.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
+#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
+
+#if defined(OS_QNX)
+#include <sys/cpuinline.h>
+#endif
+
+namespace base {
+namespace subtle {
+
+inline void MemoryBarrier() {
+ __asm__ __volatile__ ("dmb ish" ::: "memory"); // NOLINT
+}
+
+// NoBarrier versions of the operation include "memory" in the clobber list.
+// This is not required for direct usage of the NoBarrier versions of the
+// operations. However this is required for correctness when they are used as
+// part of the Acquire or Release versions, to ensure that nothing from outside
+// the call is reordered between the operation and the memory barrier. This does
+// not change the code generated, so has no or minimal impact on the
+// NoBarrier operations.
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %w[prev], %[ptr] \n\t" // Load the previous value.
+ "cmp %w[prev], %w[old_value] \n\t"
+ "bne 1f \n\t"
+ "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
+ "cbnz %w[temp], 0b \n\t" // Retry if it did not work.
+ "1: \n\t"
+ : [prev]"=&r" (prev),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [old_value]"IJr" (old_value),
+ [new_value]"r" (new_value)
+ : "cc", "memory"
+ ); // NOLINT
+
+ return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+ Atomic32 new_value) {
+ Atomic32 result;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %w[result], %[ptr] \n\t" // Load the previous value.
+ "stxr %w[temp], %w[new_value], %[ptr] \n\t" // Try to store the new value.
+ "cbnz %w[temp], 0b \n\t" // Retry if it did not work.
+ : [result]"=&r" (result),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [new_value]"r" (new_value)
+ : "memory"
+ ); // NOLINT
+
+ return result;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ Atomic32 result;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %w[result], %[ptr] \n\t" // Load the previous value.
+ "add %w[result], %w[result], %w[increment]\n\t"
+ "stxr %w[temp], %w[result], %[ptr] \n\t" // Try to store the result.
+ "cbnz %w[temp], 0b \n\t" // Retry on failure.
+ : [result]"=&r" (result),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [increment]"IJr" (increment)
+ : "memory"
+ ); // NOLINT
+
+ return result;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
+ MemoryBarrier();
+ Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
+ MemoryBarrier();
+
+ return result;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ MemoryBarrier();
+
+ return prev;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+ Atomic32 old_value,
+ Atomic32 new_value) {
+ MemoryBarrier();
+ Atomic32 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+
+ return prev;
+}
+
+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) {
+ __asm__ __volatile__ ( // NOLINT
+ "stlr %w[value], %[ptr] \n\t"
+ : [ptr]"=Q" (*ptr)
+ : [value]"r" (value)
+ : "memory"
+ ); // NOLINT
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+ return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+ Atomic32 value;
+
+ __asm__ __volatile__ ( // NOLINT
+ "ldar %w[value], %[ptr] \n\t"
+ : [value]"=r" (value)
+ : [ptr]"Q" (*ptr)
+ : "memory"
+ ); // NOLINT
+
+ return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+// 64-bit versions of the operations.
+// See the 32-bit versions for comments.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %[prev], %[ptr] \n\t"
+ "cmp %[prev], %[old_value] \n\t"
+ "bne 1f \n\t"
+ "stxr %w[temp], %[new_value], %[ptr] \n\t"
+ "cbnz %w[temp], 0b \n\t"
+ "1: \n\t"
+ : [prev]"=&r" (prev),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [old_value]"IJr" (old_value),
+ [new_value]"r" (new_value)
+ : "cc", "memory"
+ ); // NOLINT
+
+ return prev;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+ Atomic64 new_value) {
+ Atomic64 result;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %[result], %[ptr] \n\t"
+ "stxr %w[temp], %[new_value], %[ptr] \n\t"
+ "cbnz %w[temp], 0b \n\t"
+ : [result]"=&r" (result),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [new_value]"r" (new_value)
+ : "memory"
+ ); // NOLINT
+
+ return result;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ Atomic64 result;
+ int32_t temp;
+
+ __asm__ __volatile__ ( // NOLINT
+ "0: \n\t"
+ "ldxr %[result], %[ptr] \n\t"
+ "add %[result], %[result], %[increment] \n\t"
+ "stxr %w[temp], %[result], %[ptr] \n\t"
+ "cbnz %w[temp], 0b \n\t"
+ : [result]"=&r" (result),
+ [temp]"=&r" (temp),
+ [ptr]"+Q" (*ptr)
+ : [increment]"IJr" (increment)
+ : "memory"
+ ); // NOLINT
+
+ return result;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+ Atomic64 increment) {
+ MemoryBarrier();
+ Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment);
+ MemoryBarrier();
+
+ return result;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+ MemoryBarrier();
+
+ return prev;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+ Atomic64 old_value,
+ Atomic64 new_value) {
+ MemoryBarrier();
+ Atomic64 prev = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
+
+ return prev;
+}
+
+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) {
+ __asm__ __volatile__ ( // NOLINT
+ "stlr %x[value], %[ptr] \n\t"
+ : [ptr]"=Q" (*ptr)
+ : [value]"r" (value)
+ : "memory"
+ ); // NOLINT
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+ return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+ Atomic64 value;
+
+ __asm__ __volatile__ ( // NOLINT
+ "ldar %x[value], %[ptr] \n\t"
+ : [value]"=r" (value)
+ : [ptr]"Q" (*ptr)
+ : "memory"
+ ); // NOLINT
+
+ return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+ MemoryBarrier();
+ return *ptr;
+}
+
+} // namespace base::subtle
+} // namespace base
+
+#endif // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
diff --git a/chromium/base/atomicops_internals_arm_gcc.h b/chromium/base/atomicops_internals_arm_gcc.h
index 9f4fe2e586e..e654afa7d27 100644
--- a/chromium/base/atomicops_internals_arm_gcc.h
+++ b/chromium/base/atomicops_internals_arm_gcc.h
@@ -9,6 +9,10 @@
#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_
+#if defined(OS_QNX)
+#include <sys/cpuinline.h>
+#endif
+
namespace base {
namespace subtle {
@@ -40,10 +44,15 @@ namespace subtle {
//
inline void MemoryBarrier() {
- // Note: This is a function call, which is also an implicit compiler
- // barrier.
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ // Note: This is a function call, which is also an implicit compiler barrier.
typedef void (*KernelMemoryBarrierFunc)();
((KernelMemoryBarrierFunc)0xffff0fa0)();
+#elif defined(OS_QNX)
+ __cpu_membarrier();
+#else
+#error MemoryBarrier() is not implemented on this platform.
+#endif
}
// An ARM toolchain would only define one of these depending on which
@@ -54,7 +63,7 @@ inline void MemoryBarrier() {
defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) || \
defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) || \
defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || \
- defined(__ARM_ARCH_6KZ__) || defined(__ARM_ARCH_6T2__)
+ defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__)
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
diff --git a/chromium/base/atomicops_internals_mac.h b/chromium/base/atomicops_internals_mac.h
index 658ed54879f..ccbb896e4cb 100644
--- a/chromium/base/atomicops_internals_mac.h
+++ b/chromium/base/atomicops_internals_mac.h
@@ -12,7 +12,7 @@
namespace base {
namespace subtle {
-inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
@@ -26,7 +26,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
return prev_value;
}
-inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
@@ -36,13 +36,13 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
return old_value;
}
-inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr,
+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) {
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+ Atomic32 increment) {
return OSAtomicAdd32Barrier(increment, const_cast<Atomic32*>(ptr));
}
@@ -50,7 +50,7 @@ inline void MemoryBarrier() {
OSMemoryBarrier();
}
-inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
@@ -64,7 +64,7 @@ inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
return prev_value;
}
-inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Acquire_CompareAndSwap(ptr, old_value, new_value);
@@ -74,12 +74,12 @@ inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
-inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) {
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
MemoryBarrier();
}
-inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
@@ -88,13 +88,13 @@ inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
-inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
-inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
MemoryBarrier();
return *ptr;
}
@@ -103,7 +103,7 @@ inline Atomic32 Release_Load(volatile const Atomic32 *ptr) {
// 64-bit implementation on 64-bit platform
-inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
@@ -117,7 +117,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
return prev_value;
}
-inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
@@ -127,18 +127,18 @@ inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
return old_value;
}
-inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr,
+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,
+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,
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
@@ -152,7 +152,7 @@ inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
return prev_value;
}
-inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
// The lib kern interface does not distinguish between
@@ -164,12 +164,12 @@ inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
-inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) {
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
MemoryBarrier();
}
-inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
@@ -178,13 +178,13 @@ inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
-inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
-inline Atomic64 Release_Load(volatile const Atomic64 *ptr) {
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
MemoryBarrier();
return *ptr;
}
diff --git a/chromium/base/atomicops_internals_tsan.h b/chromium/base/atomicops_internals_tsan.h
index 44d64009c70..24382fd9c01 100644
--- a/chromium/base/atomicops_internals_tsan.h
+++ b/chromium/base/atomicops_internals_tsan.h
@@ -8,358 +8,168 @@
#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")
+#include <sanitizer/tsan_interface_atomic.h>
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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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) {
+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);
@@ -373,6 +183,4 @@ inline void MemoryBarrier() {
} // namespace base::subtle
} // namespace base
-#undef ATOMICOPS_COMPILER_BARRIER
-
#endif // BASE_ATOMICOPS_INTERNALS_TSAN_H_
diff --git a/chromium/base/atomicops_internals_x86_gcc.cc b/chromium/base/atomicops_internals_x86_gcc.cc
index 933ca51896d..3f47458ad19 100644
--- a/chromium/base/atomicops_internals_x86_gcc.cc
+++ b/chromium/base/atomicops_internals_x86_gcc.cc
@@ -5,10 +5,10 @@
// This module gets enough CPU information to optimize the
// atomicops module on x86.
+#include <stdint.h>
#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
@@ -21,16 +21,16 @@
// 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__)
+ 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))
+ 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
@@ -40,15 +40,16 @@
// 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
};
+namespace {
+
// Initialize the AtomicOps_Internalx86CPUFeatures struct.
-static void AtomicOps_Internalx86CPUFeaturesInit() {
- uint32 eax;
- uint32 ebx;
- uint32 ecx;
- uint32 edx;
+void AtomicOps_Internalx86CPUFeaturesInit() {
+ uint32_t eax;
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
// Get vendor string (issue CPUID with eax = 0)
cpuid(eax, ebx, ecx, edx, 0);
@@ -80,13 +81,8 @@ static void AtomicOps_Internalx86CPUFeaturesInit() {
} 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() {
diff --git a/chromium/base/atomicops_internals_x86_gcc.h b/chromium/base/atomicops_internals_x86_gcc.h
index ac02b17f5db..7386fabea9c 100644
--- a/chromium/base/atomicops_internals_x86_gcc.h
+++ b/chromium/base/atomicops_internals_x86_gcc.h
@@ -17,7 +17,6 @@
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;
@@ -92,10 +91,6 @@ 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");
}
@@ -105,28 +100,6 @@ inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 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.
diff --git a/chromium/base/atomicops_internals_x86_msvc.h b/chromium/base/atomicops_internals_x86_msvc.h
index 3a2c72ddb47..0269d894a1f 100644
--- a/chromium/base/atomicops_internals_x86_msvc.h
+++ b/chromium/base/atomicops_internals_x86_msvc.h
@@ -9,6 +9,10 @@
#include <windows.h>
+#include <intrin.h>
+
+#include "base/macros.h"
+
#if defined(ARCH_CPU_64_BITS)
// 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
@@ -24,7 +28,7 @@ namespace subtle {
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
- LONG result = InterlockedCompareExchange(
+ LONG result = _InterlockedCompareExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value),
static_cast<LONG>(old_value));
@@ -33,7 +37,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
- LONG result = InterlockedExchange(
+ LONG result = _InterlockedExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value));
return static_cast<Atomic32>(result);
@@ -41,7 +45,7 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
Atomic32 increment) {
- return InterlockedExchangeAdd(
+ return _InterlockedExchangeAdd(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(increment)) + increment;
}
diff --git a/chromium/base/atomicops_unittest.cc b/chromium/base/atomicops_unittest.cc
index d73a098c488..3fd55974722 100644
--- a/chromium/base/atomicops_unittest.cc
+++ b/chromium/base/atomicops_unittest.cc
@@ -4,9 +4,9 @@
#include "base/atomicops.h"
+#include <stdint.h>
#include <string.h>
-#include "base/port.h"
#include "testing/gtest/include/gtest/gtest.h"
template <class AtomicType>
@@ -91,7 +91,7 @@ static void TestCompareAndSwap() {
// 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) <<
+ const AtomicType k_test_val = (static_cast<uint64_t>(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5);
@@ -114,7 +114,7 @@ static void TestAtomicExchange() {
// 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) <<
+ const AtomicType k_test_val = (static_cast<uint64_t>(1) <<
(NUM_BITS(AtomicType) - 2)) + 11;
value = k_test_val;
new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val);
@@ -131,7 +131,7 @@ static void TestAtomicExchange() {
template <class AtomicType>
static void TestAtomicIncrementBounds() {
// Test at rollover boundary between int_max and int_min
- AtomicType test_val = (GG_ULONGLONG(1) <<
+ AtomicType test_val = (static_cast<uint64_t>(1) <<
(NUM_BITS(AtomicType) - 1));
AtomicType value = -1 ^ test_val;
AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
@@ -142,7 +142,7 @@ static void TestAtomicIncrementBounds() {
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);
+ test_val = static_cast<uint64_t>(1) << (NUM_BITS(AtomicType) / 2);
value = test_val - 1;
new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1);
EXPECT_EQ(test_val, value);
diff --git a/chromium/base/base.gyp b/chromium/base/base.gyp
index 407d49d0582..5544a4b5a27 100644
--- a/chromium/base/base.gyp
+++ b/chromium/base/base.gyp
@@ -40,14 +40,6 @@
['chromeos==1', {
'sources/': [ ['include', '_chromeos\\.cc$'] ]
}],
- ['toolkit_uses_gtk==1', {
- 'dependencies': [
- '../build/linux/system.gyp:gtk',
- ],
- 'export_dependent_settings': [
- '../build/linux/system.gyp:gtk',
- ],
- }],
],
'dependencies': [
'symbolize',
@@ -56,9 +48,6 @@
'defines': [
'USE_SYMBOLIZE',
],
- 'cflags': [
- '-Wno-write-strings',
- ],
}, { # desktop_linux == 0 and chromeos == 0
'sources/': [
['exclude', '/xdg_user_dirs/'],
@@ -73,22 +62,6 @@
'../build/linux/system.gyp:glib',
],
}],
- ['use_x11==1', {
- 'dependencies': [
- '../build/linux/system.gyp:x11',
- ],
- 'export_dependent_settings': [
- '../build/linux/system.gyp:x11',
- ],
- }],
- ['use_aura==1 and use_x11==1', {
- 'dependencies': [
- '../build/linux/system.gyp:xrandr',
- ],
- 'export_dependent_settings': [
- '../build/linux/system.gyp:xrandr',
- ],
- }],
['OS == "android" and _toolset == "host"', {
# Always build base as a static_library for host toolset, even if
# we're doing a component build. Specifically, we only care about the
@@ -105,19 +78,6 @@
# 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$'],
@@ -131,7 +91,7 @@
}],
['OS == "android" and _toolset == "target"', {
'conditions': [
- ['target_arch == "ia32"', {
+ ['target_arch == "ia32" or target_arch == "x64"', {
'sources/': [
['include', '^atomicops_internals_x86_gcc\\.cc$'],
],
@@ -146,9 +106,6 @@
'base_jni_headers',
'../third_party/ashmem/ashmem.gyp:ashmem',
],
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/base',
- ],
'link_settings': {
'libraries': [
'-llog',
@@ -186,7 +143,7 @@
],
},
'conditions': [
- ['linux_use_tcmalloc==0', {
+ ['use_allocator!="tcmalloc"', {
'defines': [
'NO_TCMALLOC',
],
@@ -198,6 +155,32 @@
}],
],
}],
+ ['OS == "win"', {
+ # Specify delayload for base.dll.
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'powrprof.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'powrprof.lib',
+ ],
+ },
+ },
+ # Specify delayload for components that link with base.lib.
+ 'all_dependent_settings': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'powrprof.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'powrprof.lib',
+ ],
+ },
+ },
+ },
+ }],
['OS == "mac" or (OS == "ios" and _toolset == "host")', {
'link_settings': {
'libraries': [
@@ -237,16 +220,8 @@
}],
],
}],
- ['use_system_nspr==1', {
- 'dependencies': [
- 'third_party/nspr/nspr.gyp:nspr',
- ],
- }],
],
'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',
'async_socket_io_handler.h',
@@ -258,14 +233,10 @@
'event_recorder_win.cc',
'linux_util.cc',
'linux_util.h',
- 'md5.cc',
- 'md5.h',
'message_loop/message_pump_android.cc',
'message_loop/message_pump_android.h',
'message_loop/message_pump_glib.cc',
'message_loop/message_pump_glib.h',
- 'message_loop/message_pump_gtk.cc',
- 'message_loop/message_pump_gtk.h',
'message_loop/message_pump_io_ios.cc',
'message_loop/message_pump_io_ios.h',
'message_loop/message_pump_observer.h',
@@ -273,8 +244,6 @@
'message_loop/message_pump_libevent.h',
'message_loop/message_pump_mac.h',
'message_loop/message_pump_mac.mm',
- 'message_loop/message_pump_x11.cc',
- 'message_loop/message_pump_x11.h',
'metrics/field_trial.cc',
'metrics/field_trial.h',
'posix/file_descriptor_shuffle.cc',
@@ -290,6 +259,7 @@
'variables': {
'enable_wexit_time_destructors': 1,
'optimize': 'max',
+ 'base_i18n_target': 1,
},
'dependencies': [
'base',
@@ -298,58 +268,29 @@
'../third_party/icu/icu.gyp:icuuc',
],
'conditions': [
- ['toolkit_uses_gtk==1', {
- 'dependencies': [
- # i18n/rtl.cc uses gtk
- '../build/linux/system.gyp:gtk',
- ],
- }],
['OS == "win"', {
# TODO(jschuh): crbug.com/167187 fix size_t to int truncations.
'msvs_disabled_warnings': [
4267,
],
}],
+ ['icu_use_data_file_flag==1', {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_FILE'],
+ }, { # else icu_use_data_file_flag !=1
+ 'conditions': [
+ ['OS=="win"', {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_SHARED'],
+ }, {
+ 'defines': ['ICU_UTIL_DATA_IMPL=ICU_UTIL_DATA_STATIC'],
+ }],
+ ],
+ }],
],
'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/i18n_constants.cc',
- 'i18n/i18n_constants.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_compare.cc',
- 'i18n/string_compare.h',
- 'i18n/string_search.cc',
- 'i18n/string_search.h',
- 'i18n/time_formatting.cc',
- 'i18n/time_formatting.h',
- 'i18n/timezone.cc',
- 'i18n/timezone.h',
- ],
+
+
},
{
'target_name': 'base_message_loop_tests',
@@ -390,6 +331,7 @@
'prefs/persistent_pref_store.h',
'prefs/pref_change_registrar.cc',
'prefs/pref_change_registrar.h',
+ 'prefs/pref_filter.h',
'prefs/pref_member.cc',
'prefs/pref_member.h',
'prefs/pref_notifier.h',
@@ -414,6 +356,7 @@
'prefs/scoped_user_pref_update.h',
'prefs/value_map_pref_store.cc',
'prefs/value_map_pref_store.h',
+ 'prefs/writeable_pref_store.h',
],
},
{
@@ -471,8 +414,7 @@
'target_name': 'base_unittests',
'type': '<(gtest_target_type)',
'sources': [
- # Tests.
- 'android/activity_status_unittest.cc',
+ 'android/application_status_listener_unittest.cc',
'android/jni_android_unittest.cc',
'android/jni_array_unittest.cc',
'android/jni_string_unittest.cc',
@@ -484,6 +426,7 @@
'atomicops_unittest.cc',
'barrier_closure_unittest.cc',
'base64_unittest.cc',
+ 'big_endian_unittest.cc',
'bind_unittest.cc',
'bind_unittest.nc',
'bits_unittest.cc',
@@ -506,6 +449,7 @@
'debug/proc_maps_linux_unittest.cc',
'debug/stack_trace_unittest.cc',
'debug/trace_event_memory_unittest.cc',
+ 'debug/trace_event_synthetic_delay_unittest.cc',
'debug/trace_event_system_stats_monitor_unittest.cc',
'debug/trace_event_unittest.cc',
'debug/trace_event_unittest.h',
@@ -516,12 +460,14 @@
'file_version_info_unittest.cc',
'files/dir_reader_posix_unittest.cc',
'files/file_path_unittest.cc',
+ 'files/file_proxy_unittest.cc',
'files/file_unittest.cc',
'files/file_util_proxy_unittest.cc',
'files/important_file_writer_unittest.cc',
'files/scoped_temp_dir_unittest.cc',
'gmock_unittest.cc',
'guid_unittest.cc',
+ 'hash_unittest.cc',
'id_map_unittest.cc',
'i18n/break_iterator_unittest.cc',
'i18n/char_iterator_unittest.cc',
@@ -530,6 +476,7 @@
'i18n/icu_string_conversions_unittest.cc',
'i18n/number_formatting_unittest.cc',
'i18n/rtl_unittest.cc',
+ 'i18n/streaming_utf8_validator_unittest.cc',
'i18n/string_search_unittest.cc',
'i18n/time_formatting_unittest.cc',
'i18n/timezone_unittest.cc',
@@ -552,9 +499,8 @@
'mac/scoped_sending_event_unittest.mm',
'md5_unittest.cc',
'memory/aligned_memory_unittest.cc',
- 'memory/discardable_memory_allocator_android_unittest.cc',
+ 'memory/discardable_memory_manager_unittest.cc',
'memory/discardable_memory_unittest.cc',
- 'memory/discardable_memory_provider_unittest.cc',
'memory/linked_ptr_unittest.cc',
'memory/ref_counted_memory_unittest.cc',
'memory/ref_counted_unittest.cc',
@@ -577,6 +523,7 @@
'metrics/field_trial_unittest.cc',
'metrics/histogram_base_unittest.cc',
'metrics/histogram_delta_serialization_unittest.cc',
+ 'metrics/histogram_snapshot_manager_unittest.cc',
'metrics/histogram_unittest.cc',
'metrics/sparse_histogram_unittest.cc',
'metrics/stats_table_unittest.cc',
@@ -608,9 +555,9 @@
'process/process_util_unittest.cc',
'profiler/tracked_time_unittest.cc',
'rand_util_unittest.cc',
- 'safe_numerics_unittest.cc',
- 'safe_numerics_unittest.nc',
+ 'numerics/safe_numerics_unittest.cc',
'scoped_clear_errno_unittest.cc',
+ 'scoped_generic_unittest.cc',
'scoped_native_library_unittest.cc',
'scoped_observer.h',
'security_unittest.cc',
@@ -631,6 +578,7 @@
'strings/sys_string_conversions_unittest.cc',
'strings/utf_offset_string_conversions_unittest.cc',
'strings/utf_string_conversions_unittest.cc',
+ 'supports_user_data_unittest.cc',
'sync_socket_unittest.cc',
'synchronization/cancellation_flag_unittest.cc',
'synchronization/condition_variable_unittest.cc',
@@ -639,10 +587,12 @@
'synchronization/waitable_event_watcher_unittest.cc',
'sys_info_unittest.cc',
'system_monitor/system_monitor_unittest.cc',
+ 'task/cancelable_task_tracker_unittest.cc',
'task_runner_util_unittest.cc',
'template_util_unittest.cc',
'test/expectations/expectation_unittest.cc',
'test/expectations/parser_unittest.cc',
+ 'test/statistics_delta_reader_unittest.cc',
'test/test_reg_util_win_unittest.cc',
'test/trace_event_analyzer_unittest.cc',
'threading/non_thread_safe_unittest.cc',
@@ -662,6 +612,7 @@
'time/time_unittest.cc',
'time/time_win_unittest.cc',
'timer/hi_res_timer_manager_unittest.cc',
+ 'timer/mock_timer_unittest.cc',
'timer/timer_unittest.cc',
'tools_sanity_unittest.cc',
'tracked_objects_unittest.cc',
@@ -710,21 +661,10 @@
'module_dir': 'base'
},
'conditions': [
- ['desktop_linux == 1 or chromeos == 1', {
- 'defines': [
- 'USE_SYMBOLIZE',
- ],
- }],
['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',
- ],
- }],
+ '../testing/android/native_test.gyp:native_test_native_code',
],
}],
['OS == "ios" and _toolset != "host"', {
@@ -752,27 +692,19 @@
],
}],
['desktop_linux == 1 or chromeos == 1', {
+ 'defines': [
+ 'USE_SYMBOLIZE',
+ ],
'sources!': [
'file_version_info_unittest.cc',
],
'conditions': [
- [ 'toolkit_uses_gtk==1', {
+ [ 'desktop_linux==1', {
'sources': [
'nix/xdg_util_unittest.cc',
],
- 'dependencies': [
- '../build/linux/system.gyp:gtk',
- ]
}],
],
- 'dependencies': [
- '../build/linux/system.gyp:ssl',
- ],
- }],
- ['use_x11 == 1', {
- 'dependencies': [
- '../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck',
- ],
}],
['use_glib == 1', {
'dependencies': [
@@ -788,18 +720,13 @@
'message_loop/message_pump_glib_unittest.cc',
]
}],
- ['OS == "linux" and linux_use_tcmalloc==1', {
+ ['OS == "linux" and use_allocator!="none"', {
'dependencies': [
'allocator/allocator.gyp:allocator',
],
},
],
['OS == "win"', {
- # This is needed to trigger the dll copy step on windows.
- # TODO(mark): This should not be necessary.
- 'dependencies': [
- '../third_party/icu/icu.gyp:icudata',
- ],
'sources!': [
'file_descriptor_shuffle_unittest.cc',
'files/dir_reader_posix_unittest.cc',
@@ -810,38 +737,29 @@
'msvs_disabled_warnings': [
4267,
],
- # This is needed so base_unittests uses the allocator shim, as
- # SecurityTest.MemoryAllocationRestriction* tests are dependent
- # on tcmalloc.
- # TODO(wfh): crbug.com/246278 Move tcmalloc specific tests into
- # their own test suite.
'conditions': [
+ # This is needed so base_unittests uses the allocator shim, as
+ # SecurityTest.MemoryAllocationRestriction* tests are dependent
+ # on tcmalloc.
+ # TODO(wfh): crbug.com/246278 Move tcmalloc specific tests into
+ # their own test suite.
['win_use_allocator_shim==1', {
'dependencies': [
'allocator/allocator.gyp:allocator',
],
}],
+ ['icu_use_data_file_flag==0', {
+ # This is needed to trigger the dll copy step on windows.
+ # TODO(mark): This should not be necessary.
+ 'dependencies': [
+ '../third_party/icu/icu.gyp:icudata',
+ ],
+ }],
],
}, { # OS != "win"
'dependencies': [
'../third_party/libevent/libevent.gyp:libevent'
],
- 'sources/': [
- ['exclude', '^win/'],
- ],
- 'sources!': [
- 'win/win_util_unittest.cc',
- ],
- }],
- ['use_aura==1 and use_x11==1', {
- 'sources': [
- 'x11/edid_parser_x11_unittest.cc',
- ],
- }],
- ['use_system_nspr==1', {
- 'dependencies': [
- 'third_party/nspr/nspr.gyp:nspr',
- ],
}],
], # conditions
'target_conditions': [
@@ -849,12 +767,18 @@
'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', '^mac/foundation_util_unittest\\.mm$',],
+ ['include', '^mac/objc_property_releaser_unittest\\.mm$'],
['include', '^mac/scoped_nsobject_unittest\\.mm$'],
['include', '^sys_string_conversions_mac_unittest\\.mm$'],
],
}],
+ ['OS == "android" and _toolset == "target"', {
+ 'sources': [
+ 'memory/discardable_memory_ashmem_allocator_unittest.cc',
+ ],
+ }],
['OS == "android"', {
'sources/': [
['include', '^debug/proc_maps_linux_unittest\\.cc$'],
@@ -863,6 +787,41 @@
], # target_conditions
},
{
+ 'target_name': 'base_perftests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'base',
+ 'test_support_base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'threading/thread_perftest.cc',
+ 'test/run_all_unittests.cc',
+ '../testing/perf/perf_test.cc'
+ ],
+ 'conditions': [
+ ['OS == "android"', {
+ 'dependencies': [
+ '../testing/android/native_test.gyp:native_test_native_code',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'base_i18n_perftests',
+ 'type': '<(gtest_target_type)',
+ 'dependencies': [
+ 'test_support_base',
+ 'test_support_perf',
+ '../testing/gtest.gyp:gtest',
+ 'base_i18n',
+ 'base',
+ ],
+ 'sources': [
+ 'i18n/streaming_utf8_validator_perftest.cc',
+ ],
+ },
+ {
'target_name': 'test_support_base',
'type': 'static_library',
'dependencies': [
@@ -878,12 +837,6 @@
'base',
],
'conditions': [
- ['toolkit_uses_gtk==1', {
- 'dependencies': [
- # test_suite initializes GTK.
- '../build/linux/system.gyp:gtk',
- ],
- }],
['os_posix==0', {
'sources!': [
'test/scoped_locale.cc',
@@ -900,9 +853,6 @@
'base_unittests_jni_headers',
'base_java_unittest_support',
],
- 'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/base',
- ],
}],
],
'sources': [
@@ -952,6 +902,8 @@
'test/simple_test_clock.h',
'test/simple_test_tick_clock.cc',
'test/simple_test_tick_clock.h',
+ 'test/statistics_delta_reader.cc',
+ 'test/statistics_delta_reader.h',
'test/task_runner_test_template.cc',
'test/task_runner_test_template.h',
'test/test_file_util.cc',
@@ -1020,17 +972,51 @@
'PERF_TEST',
],
},
+ },
+ {
+ 'target_name': 'sanitizer_options',
+ 'type': 'static_library',
+ 'toolsets': ['host', 'target'],
+ 'variables': {
+ # Every target is going to depend on sanitizer_options, so allow
+ # this one to depend on itself.
+ 'prune_self_dependency': 1,
+ # Do not let 'none' targets depend on this one, they don't need to.
+ 'link_dependency': 1,
+ },
+ 'sources': [
+ 'debug/sanitizer_options.cc',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ # Some targets may want to opt-out from ASan, TSan and MSan and link
+ # without the corresponding runtime libraries. We drop the libc++
+ # dependency and omit the compiler flags to avoid bringing instrumented
+ # code to those targets.
'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',
+ ['use_custom_libcxx==1', {
+ 'dependencies!': [
+ '../third_party/libc++/libc++.gyp:libcxx_proxy',
+ ],
+ }],
+ ['tsan==1', {
+ 'sources': [
+ 'debug/tsan_suppressions.cc',
],
}],
],
+ 'cflags!': [
+ '-fsanitize=address',
+ '-fsanitize=thread',
+ '-fsanitize=memory',
+ '-fsanitize-memory-track-origins',
+ ],
+ 'direct_dependent_settings': {
+ 'ldflags': [
+ '-Wl,-u_sanitizer_options_link_helper',
+ ],
+ },
},
],
'conditions': [
@@ -1046,12 +1032,26 @@
'base',
],
},
+ {
+ 'target_name': 'build_utf8_validator_tables',
+ 'type': 'executable',
+ 'toolsets': ['host'],
+ 'dependencies': [
+ 'base',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'sources': [
+ 'i18n/build_utf8_validator_tables.cc'
+ ],
+ },
],
}],
['OS == "win" and target_arch=="ia32"', {
'targets': [
+ # The base_win64 target here allows us to use base for Win64 targets
+ # (the normal build is 32 bits).
{
- 'target_name': 'base_nacl_win64',
+ 'target_name': 'base_win64',
'type': '<(component)',
'variables': {
'base_target': 1,
@@ -1059,6 +1059,7 @@
'dependencies': [
'base_static_win64',
'allocator/allocator.gyp:allocator_extension_thunks_win64',
+ '../third_party/modp_b64/modp_b64.gyp:modp_b64_win64',
'third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations_win64',
],
# TODO(gregoryd): direct_dependent_settings should be shared with the
@@ -1069,12 +1070,9 @@
],
},
'defines': [
+ 'BASE_WIN64',
'<@(nacl_win64_defines)',
],
- 'sources!': [
- # base64.cc depends on modp_b64.
- 'base64.cc',
- ],
'configurations': {
'Common_Base': {
'msvs_target_platform': 'x64',
@@ -1087,6 +1085,61 @@
],
}],
],
+ # Specify delayload for base_win64.dll.
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'powrprof.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'powrprof.lib',
+ ],
+ },
+ },
+ # Specify delayload for components that link with base_win64.lib.
+ 'all_dependent_settings': {
+ 'msvs_settings': {
+ 'VCLinkerTool': {
+ 'DelayLoadDLLs': [
+ 'powrprof.dll',
+ ],
+ 'AdditionalDependencies': [
+ 'powrprof.lib',
+ ],
+ },
+ },
+ },
+ # TODO(rvargas): Bug 78117. Remove this.
+ 'msvs_disabled_warnings': [
+ 4244,
+ 4996,
+ 4267,
+ ],
+ 'sources': [
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.cc',
+ 'third_party/xdg_user_dirs/xdg_user_dir_lookup.h',
+ 'async_socket_io_handler.h',
+ 'async_socket_io_handler_posix.cc',
+ 'async_socket_io_handler_win.cc',
+ '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_loop/message_pump_observer.h',
+ 'message_loop/message_pump_libevent.cc',
+ 'message_loop/message_pump_libevent.h',
+ '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_nacl_win64',
@@ -1138,7 +1191,7 @@
},
},
'defines': [
- 'NACL_WIN64',
+ '<@(nacl_win64_defines)',
],
# TODO(rvargas): Bug 78117. Remove this.
'msvs_disabled_warnings': [
@@ -1170,6 +1223,9 @@
'cflags!': [
'-Wextra',
],
+ 'defines': [
+ 'GLOG_BUILD_CONFIG_INCLUDE="build/build_config.h"',
+ ],
'sources': [
'third_party/symbolize/config.h',
'third_party/symbolize/demangle.cc',
@@ -1221,12 +1277,14 @@
'target_name': 'base_jni_headers',
'type': 'none',
'sources': [
- 'android/java/src/org/chromium/base/ActivityStatus.java',
+ 'android/java/src/org/chromium/base/ApplicationStatus.java',
'android/java/src/org/chromium/base/BuildInfo.java',
'android/java/src/org/chromium/base/CommandLine.java',
'android/java/src/org/chromium/base/ContentUriUtils.java',
'android/java/src/org/chromium/base/CpuFeatures.java',
+ 'android/java/src/org/chromium/base/EventLog.java',
'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java',
+ 'android/java/src/org/chromium/base/library_loader/LibraryLoader.java',
'android/java/src/org/chromium/base/MemoryPressureListener.java',
'android/java/src/org/chromium/base/JavaHandlerThread.java',
'android/java/src/org/chromium/base/PathService.java',
@@ -1235,17 +1293,10 @@
'android/java/src/org/chromium/base/SystemMessageHandler.java',
'android/java/src/org/chromium/base/SysUtils.java',
'android/java/src/org/chromium/base/ThreadUtils.java',
- ],
- 'conditions': [
- ['google_tv==1', {
- 'sources': [
- 'android/java/src/org/chromium/base/ContextTypes.java',
- ],
- }],
+ 'android/java/src/org/chromium/base/TraceEvent.java',
],
'variables': {
'jni_gen_package': 'base',
- 'jni_generator_ptr_type': 'long',
},
'includes': [ '../build/jni_generator.gypi' ],
},
@@ -1261,14 +1312,31 @@
'includes': [ '../build/jni_generator.gypi' ],
},
{
+ 'target_name': 'base_native_libraries_gen',
+ 'type': 'none',
+ 'sources': [
+ 'android/java/templates/NativeLibraries.template',
+ ],
+ 'variables': {
+ 'package_name': 'org/chromium/base/library_loader',
+ 'include_path': 'android/java/templates',
+ 'template_deps': [
+ 'android/java/templates/native_libraries_array.h'
+ ],
+ },
+ 'includes': [ '../build/android/java_cpp_template.gypi' ],
+ },
+ {
'target_name': 'base_java',
'type': 'none',
'variables': {
'java_in_dir': '../base/android/java',
+ 'jar_excluded_classes': [ '*/NativeLibraries.class' ],
},
'dependencies': [
- 'base_java_activity_state',
+ 'base_java_application_state',
'base_java_memory_pressure_level_list',
+ 'base_native_libraries_gen',
],
'includes': [ '../build/java.gypi' ],
'conditions': [
@@ -1291,18 +1359,18 @@
'includes': [ '../build/java.gypi' ],
},
{
- 'target_name': 'base_java_activity_state',
+ 'target_name': 'base_java_application_state',
'type': 'none',
- # This target is used to auto-generate ActivityState.java
+ # This target is used to auto-generate ApplicationState.java
# from a template file. The source file contains a list of
# Java constant declarations matching the ones in
- # android/activity_state_list.h.
+ # android/application_state_list.h.
'sources': [
- 'android/java/src/org/chromium/base/ActivityState.template',
+ 'android/java/src/org/chromium/base/ApplicationState.template',
],
'variables': {
'package_name': 'org/chromium/base',
- 'template_deps': ['android/activity_state_list.h'],
+ 'template_deps': ['android/application_state_list.h'],
},
'includes': [ '../build/android/java_cpp_template.gypi' ],
},
@@ -1341,6 +1409,48 @@
},
'includes': [ '../build/java.gypi' ],
},
+ {
+ 'target_name': 'chromium_android_linker',
+ 'type': 'shared_library',
+ 'conditions': [
+ ['android_webview_build == 0 and target_arch != "x64" and \
+ target_arch != "arm64"', {
+ # Avoid breaking the webview/64-bit build because they
+ # don't have <(android_ndk_root)/crazy_linker.gyp.
+ # Note that webview never uses the linker anyway.
+ # Note there is no 64-bit support in the linker.
+ 'sources': [
+ 'android/linker/linker_jni.cc',
+ ],
+ # The crazy linker is never instrumented.
+ 'cflags!': [
+ '-finstrument-functions',
+ ],
+ 'dependencies': [
+ # The NDK contains the crazy_linker here:
+ # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker'
+ # However, we use our own fork. See bug 384700.
+ '../third_party/android_crazy_linker/crazy_linker.gyp:crazy_linker',
+ ],
+ }],
+ ],
+ },
+
+ ],
+ }],
+ ['OS == "android"', {
+ 'targets': [
+ {
+ 'target_name': 'base_perftests_apk',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_perftests',
+ ],
+ 'variables': {
+ 'test_suite_name': 'base_perftests',
+ },
+ 'includes': [ '../build/apk_test.gypi' ],
+ },
],
}],
['OS == "win"', {
@@ -1359,13 +1469,7 @@
},
],
}],
- # 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"', {
+ ['OS == "android"', {
'targets': [
{
'target_name': 'base_unittests_apk',
@@ -1376,7 +1480,6 @@
],
'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' ],
},
diff --git a/chromium/base/base.gypi b/chromium/base/base.gypi
index 11edab00968..c033dcbce1b 100644
--- a/chromium/base/base.gypi
+++ b/chromium/base/base.gypi
@@ -6,6 +6,7 @@
'target_defaults': {
'variables': {
'base_target': 0,
+ 'base_i18n_target': 0,
},
'target_conditions': [
# This part is shared between the targets defined below.
@@ -17,24 +18,16 @@
'third_party/dmg_fp/dtoa_wrapper.cc',
'third_party/icu/icu_utf.cc',
'third_party/icu/icu_utf.h',
- 'third_party/nspr/prcpucfg.h',
- 'third_party/nspr/prcpucfg_freebsd.h',
- 'third_party/nspr/prcpucfg_linux.h',
- 'third_party/nspr/prcpucfg_mac.h',
- 'third_party/nspr/prcpucfg_nacl.h',
- 'third_party/nspr/prcpucfg_openbsd.h',
- 'third_party/nspr/prcpucfg_solaris.h',
- 'third_party/nspr/prcpucfg_win.h',
'third_party/nspr/prtime.cc',
'third_party/nspr/prtime.h',
- 'third_party/nspr/prtypes.h',
+ 'third_party/superfasthash/superfasthash.c',
'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/activity_status.cc',
- 'android/activity_status.h',
+ 'android/application_status_listener.cc',
+ 'android/application_status_listener.h',
'android/base_jni_registrar.cc',
'android/base_jni_registrar.h',
'android/build_info.cc',
@@ -44,6 +37,8 @@
'android/content_uri_utils.cc',
'android/content_uri_utils.h',
'android/cpu_features.cc',
+ 'android/event_log.cc',
+ 'android/event_log.h',
'android/fifo_utils.cc',
'android/fifo_utils.h',
'android/important_file_writer_android.cc',
@@ -54,12 +49,14 @@
'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/jni_weak_ref.cc',
+ 'android/jni_weak_ref.h',
+ 'android/library_loader/library_loader_hooks.cc',
+ 'android/library_loader/library_loader_hooks.h',
'android/memory_pressure_listener_android.cc',
'android/memory_pressure_listener_android.h',
'android/java_handler_thread.cc',
@@ -71,6 +68,8 @@
'android/sys_utils.cc',
'android/sys_utils.h',
'android/thread_utils.h',
+ 'android/trace_event_binding.cc',
+ 'android/trace_event_binding.h',
'at_exit.cc',
'at_exit.h',
'atomic_ref_count.h',
@@ -84,6 +83,8 @@
'atomicops_internals_x86_msvc.h',
'barrier_closure.cc',
'barrier_closure.h',
+ 'base64.cc',
+ 'base64.h',
'base_export.h',
'base_paths.cc',
'base_paths.h',
@@ -96,9 +97,9 @@
'base_paths_win.cc',
'base_paths_win.h',
'base_switches.h',
- 'base64.cc',
- 'base64.h',
'basictypes.h',
+ 'big_endian.cc',
+ 'big_endian.h',
'bind.h',
'bind_helpers.cc',
'bind_helpers.h',
@@ -126,17 +127,21 @@
'cpu.cc',
'cpu.h',
'critical_closure.h',
- 'critical_closure_ios.mm',
+ 'critical_closure_internal_ios.mm',
'debug/alias.cc',
'debug/alias.h',
+ 'debug/asan_invalid_access.cc',
+ 'debug/asan_invalid_access.h',
'debug/crash_logging.cc',
'debug/crash_logging.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',
+ 'debug/dump_without_crashing.cc',
+ 'debug/dump_without_crashing.h',
+ 'debug/gdi_debug_util_win.cc',
+ 'debug/gdi_debug_util_win.h',
# This file depends on files from the 'allocator' target,
# but this target does not depend on 'allocator' (see
# allocator.gyp for details).
@@ -149,7 +154,6 @@
'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.h',
@@ -157,6 +161,8 @@
'debug/trace_event_impl.cc',
'debug/trace_event_impl.h',
'debug/trace_event_impl_constants.cc',
+ 'debug/trace_event_synthetic_delay.cc',
+ 'debug/trace_event_synthetic_delay.h',
'debug/trace_event_system_stats_monitor.cc',
'debug/trace_event_memory.cc',
'debug/trace_event_memory.h',
@@ -192,11 +198,17 @@
'files/file_path_constants.cc',
'files/file_path_watcher.cc',
'files/file_path_watcher.h',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
'files/file_path_watcher_linux.cc',
+ 'files/file_path_watcher_mac.cc',
'files/file_path_watcher_stub.cc',
'files/file_path_watcher_win.cc',
'files/file_posix.cc',
+ 'files/file_proxy.cc',
+ 'files/file_proxy.h',
'files/file_util_proxy.cc',
'files/file_util_proxy.h',
'files/file_win.cc',
@@ -206,6 +218,8 @@
'files/memory_mapped_file.h',
'files/memory_mapped_file_posix.cc',
'files/memory_mapped_file_win.cc',
+ 'files/scoped_file.cc',
+ 'files/scoped_file.h',
'files/scoped_platform_file_closer.cc',
'files/scoped_platform_file_closer.h',
'files/scoped_temp_dir.cc',
@@ -268,6 +282,8 @@
'mac/mac_logging.cc',
'mac/mac_util.h',
'mac/mac_util.mm',
+ 'mac/mach_logging.cc',
+ 'mac/mach_logging.h',
'mac/objc_property_releaser.h',
'mac/objc_property_releaser.mm',
'mac/os_crash_dumps.cc',
@@ -281,6 +297,8 @@
'mac/scoped_launch_data.h',
'mac/scoped_mach_port.cc',
'mac/scoped_mach_port.h',
+ 'mac/scoped_mach_vm.cc',
+ 'mac/scoped_mach_vm.h',
'mac/scoped_nsautorelease_pool.h',
'mac/scoped_nsautorelease_pool.mm',
'mac/scoped_nsexception_enabler.h',
@@ -288,20 +306,25 @@
'mac/scoped_nsobject.h',
'mac/scoped_sending_event.h',
'mac/scoped_sending_event.mm',
+ 'mac/scoped_typeref.h',
'mac/sdk_forward_declarations.h',
+ 'mac/sdk_forward_declarations.mm',
+ 'macros.h',
+ 'md5.cc',
+ 'md5.h',
'memory/aligned_memory.cc',
'memory/aligned_memory.h',
+ 'memory/discardable_memory.cc',
'memory/discardable_memory.h',
- 'memory/discardable_memory_allocator_android.cc',
- 'memory/discardable_memory_allocator_android.h',
'memory/discardable_memory_android.cc',
- 'memory/discardable_memory_android.h',
'memory/discardable_memory_emulated.cc',
'memory/discardable_memory_emulated.h',
'memory/discardable_memory_linux.cc',
'memory/discardable_memory_mac.cc',
- 'memory/discardable_memory_provider.cc',
- 'memory/discardable_memory_provider.h',
+ 'memory/discardable_memory_malloc.cc',
+ 'memory/discardable_memory_malloc.h',
+ 'memory/discardable_memory_manager.cc',
+ 'memory/discardable_memory_manager.h',
'memory/discardable_memory_win.cc',
'memory/linked_ptr.h',
'memory/manual_constructor.h',
@@ -313,7 +336,6 @@
'memory/ref_counted_delete_on_message_loop.h',
'memory/ref_counted_memory.cc',
'memory/ref_counted_memory.h',
- 'memory/scoped_handle.h',
'memory/scoped_open_process.h',
'memory/scoped_policy.h',
'memory/scoped_ptr.h',
@@ -341,10 +363,9 @@
'message_loop/message_pump_android.h',
'message_loop/message_pump_default.cc',
'message_loop/message_pump_default.h',
- 'message_loop/message_pump_ozone.cc',
- 'message_loop/message_pump_ozone.h',
'message_loop/message_pump_win.cc',
'message_loop/message_pump_win.h',
+ 'message_loop/timer_slack.h',
'metrics/sample_map.cc',
'metrics/sample_map.h',
'metrics/sample_vector.cc',
@@ -370,6 +391,9 @@
'metrics/stats_counters.h',
'metrics/stats_table.cc',
'metrics/stats_table.h',
+ 'metrics/user_metrics.cc',
+ 'metrics/user_metrics.h',
+ 'metrics/user_metrics_action.h',
'move.h',
'native_library.h',
'native_library_mac.mm',
@@ -428,6 +452,7 @@
'process/launch_posix.cc',
'process/launch_win.cc',
'process/memory.h',
+ 'process/memory.cc',
'process/memory_linux.cc',
'process/memory_mac.mm',
'process/memory_win.cc',
@@ -474,9 +499,13 @@
'rand_util_win.cc',
'run_loop.cc',
'run_loop.h',
- 'safe_numerics.h',
+ 'numerics/safe_conversions.h',
+ 'numerics/safe_conversions_impl.h',
+ 'numerics/safe_math.h',
+ 'numerics/safe_math_impl.h',
'safe_strerror_posix.cc',
'safe_strerror_posix.h',
+ 'scoped_generic.h',
'scoped_native_library.cc',
'scoped_native_library.h',
'sequence_checker.h',
@@ -557,6 +586,8 @@
'sys_info_openbsd.cc',
'sys_info_posix.cc',
'sys_info_win.cc',
+ 'task/cancelable_task_tracker.cc',
+ 'task/cancelable_task_tracker.h',
'task_runner.cc',
'task_runner.h',
'task_runner_util.h',
@@ -588,7 +619,9 @@
'threading/thread_id_name_manager.cc',
'threading/thread_id_name_manager.h',
'threading/thread_local.h',
+ 'threading/thread_local_android.cc',
'threading/thread_local_posix.cc',
+ 'threading/thread_local_storage.cc',
'threading/thread_local_storage.h',
'threading/thread_local_storage_posix.cc',
'threading/thread_local_storage_win.cc',
@@ -620,6 +653,8 @@
'timer/hi_res_timer_manager.h',
'timer/hi_res_timer_manager_posix.cc',
'timer/hi_res_timer_manager_win.cc',
+ 'timer/mock_timer.cc',
+ 'timer/mock_timer.h',
'timer/timer.cc',
'timer/timer.h',
'tracked_objects.cc',
@@ -678,31 +713,12 @@
'win/shortcut.h',
'win/startup_information.cc',
'win/startup_information.h',
- 'win/text_services_message_filter.cc',
- 'win/text_services_message_filter.h',
'win/win_util.cc',
'win/win_util.h',
'win/windows_version.cc',
'win/windows_version.h',
'win/wrapped_window_proc.cc',
'win/wrapped_window_proc.h',
- 'x11/x11_error_tracker.cc',
- 'x11/x11_error_tracker.h',
- 'x11/x11_error_tracker_gtk.cc',
- ],
- 'conditions': [
- ['use_aura==1 and use_x11==1', {
- 'sources': [
- 'x11/edid_parser_x11.cc',
- 'x11/edid_parser_x11.h',
- ],
- }],
- ['google_tv==1', {
- 'sources': [
- 'android/context_types.cc',
- 'android/context_types.h',
- ],
- }],
],
'defines': [
'BASE_IMPLEMENTATION',
@@ -727,14 +743,6 @@
'message_loop/message_pump_glib.cc',
],
}],
- ['<(use_x11)==0 or >(nacl_untrusted_build)==1', {
- 'sources!': [
- 'message_loop/message_pump_x11.cc',
- ],
- }],
- ['<(toolkit_uses_gtk)==0 or >(nacl_untrusted_build)==1', {
- 'sources!': ['message_loop/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.
@@ -752,7 +760,11 @@
'file_util.cc',
'file_util_posix.cc',
'files/file_enumerator_posix.cc',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
+ 'files/file_proxy.cc',
'files/file_util_proxy.cc',
'memory/shared_memory_posix.cc',
'native_library_posix.cc',
@@ -769,15 +781,24 @@
'third_party/dynamic_annotations/dynamic_annotations.c',
],
'sources/': [
- # Metrics won't work in the NaCl sandbox.
- ['exclude', '^metrics/'],
['include', '^threading/platform_thread_linux\\.cc$'],
],
}],
+ ['OS == "android" and _toolset == "target" and >(nacl_untrusted_build)==0', {
+ 'sources': [
+ 'memory/discardable_memory_ashmem_allocator.cc',
+ 'memory/discardable_memory_ashmem_allocator.h',
+ 'memory/discardable_memory_ashmem.cc',
+ 'memory/discardable_memory_ashmem.h',
+ ],
+ }],
['OS == "android" and >(nacl_untrusted_build)==0', {
'sources!': [
'base_paths_posix.cc',
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
'files/file_path_watcher_stub.cc',
'power_monitor/power_monitor_device_source_posix.cc',
],
@@ -797,8 +818,12 @@
],
}],
['OS == "android" and _toolset == "host" and host_os == "linux"', {
+ 'defines': [
+ 'OS_ANDROID_HOST=Linux',
+ ],
'sources/': [
# Pull in specific files for host builds.
+ ['include', '^atomicops_internals_x86_gcc\\.cc$'],
['include', '^threading/platform_thread_linux\\.cc$'],
],
}],
@@ -820,8 +845,10 @@
['include', '^mac/bundle_locations\\.'],
['include', '^mac/foundation_util\\.'],
['include', '^mac/mac_logging\\.'],
+ ['include', '^mac/mach_logging\\.'],
['include', '^mac/objc_property_releaser\\.'],
['include', '^mac/scoped_mach_port\\.'],
+ ['include', '^mac/scoped_mach_vm\\.'],
['include', '^mac/scoped_nsautorelease_pool\\.'],
['include', '^mac/scoped_nsobject\\.'],
['include', '^memory/discardable_memory_mac\\.'],
@@ -836,6 +863,9 @@
['include', '^process/.*_ios\.(cc|mm)$'],
['include', '^process/memory_stubs\.cc$'],
['include', '^process/process_handle_posix\.cc$'],
+ ['exclude', 'files/file_path_watcher_fsevents.cc'],
+ ['exclude', 'files/file_path_watcher_fsevents.h'],
+ ['include', 'files/file_path_watcher_mac.cc'],
],
'sources': [
'process/memory_stubs.cc',
@@ -851,13 +881,11 @@
['include', '(^|/)(cocoa|mac)/'],
['exclude', '_ios(_unittest)?\\.(h|cc|mm?)$'],
['exclude', '(^|/)ios/'],
+ ['exclude', 'files/file_path_watcher_fsevents.cc'],
+ ['exclude', 'files/file_path_watcher_fsevents.h'],
+ ['include', 'files/file_path_watcher_mac.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', {
@@ -868,21 +896,16 @@
'sources/': [ ['exclude', '_openbsd\\.cc$'] ],
},
],
- ['OS != "win" or >(nacl_untrusted_build)==1', {
- 'sources/': [ ['exclude', '^win/'] ],
- },
- ],
- ['OS != "android" or >(nacl_untrusted_build)==1', {
- '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_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
'files/file_path_watcher_stub.cc',
'message_loop/message_pump_libevent.cc',
'posix/file_descriptor_shuffle.cc',
@@ -895,12 +918,14 @@
['<(use_ozone) == 1', {
'sources!': [
'message_loop/message_pump_glib.cc',
- 'message_loop/message_pump_x11.cc',
]
}],
['OS == "linux" and >(nacl_untrusted_build)==0', {
'sources!': [
+ 'files/file_path_watcher_fsevents.cc',
+ 'files/file_path_watcher_fsevents.h',
'files/file_path_watcher_kqueue.cc',
+ 'files/file_path_watcher_kqueue.h',
'files/file_path_watcher_stub.cc',
],
}],
@@ -924,28 +949,55 @@
['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$'] ],
}],
- ['<(use_system_nspr)==1 and >(nacl_untrusted_build)==0', {
- 'sources/': [
- ['exclude', '^third_party/nspr/'],
- ],
- }],
- ['<(toolkit_uses_gtk) == 1', {
- 'sources!': [
- 'x11/x11_error_tracker.cc',
- ],
- }],
],
}],
+ ['base_i18n_target==1', {
+ '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/case_conversion.cc',
+ 'i18n/case_conversion.h',
+ 'i18n/char_iterator.cc',
+ 'i18n/char_iterator.h',
+ 'i18n/file_util_icu.cc',
+ 'i18n/file_util_icu.h',
+ 'i18n/i18n_constants.cc',
+ 'i18n/i18n_constants.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/streaming_utf8_validator.cc',
+ 'i18n/streaming_utf8_validator.h',
+ 'i18n/string_compare.cc',
+ 'i18n/string_compare.h',
+ 'i18n/string_search.cc',
+ 'i18n/string_search.h',
+ 'i18n/time_formatting.cc',
+ 'i18n/time_formatting.h',
+ 'i18n/timezone.cc',
+ 'i18n/timezone.h',
+ 'i18n/utf8_validator_tables.cc',
+ 'i18n/utf8_validator_tables.h',
+ ],
+ }]
],
},
}
diff --git a/chromium/base/base.isolate b/chromium/base/base.isolate
new file mode 100644
index 00000000000..aeab5ef3c2e
--- /dev/null
+++ b/chromium/base/base.isolate
@@ -0,0 +1,29 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+{
+ 'includes': [
+ # While the target 'base' doesn't depend on ../third_party/icu/icu.gyp
+ # itself, virtually all targets using it has to include icu. The only
+ # exception is the Windows sandbox (?).
+ '../third_party/icu/icu.isolate',
+ ],
+ 'conditions': [
+ ['OS=="win" and component=="shared_library"', {
+ 'variables': {
+ 'isolate_dependency_tracked': [
+ # Copy the VS runtime DLLs into the isolate so that they
+ # don't have to be preinstalled on the target machine. Note
+ # that depending on whether the build is Debug or Release,
+ # half of these dependencies will be broken, so these
+ # dependencies require --ignore-broken-items to be passed to
+ # the isolate driver.
+ '<(PRODUCT_DIR)/msvcp120d.dll',
+ '<(PRODUCT_DIR)/msvcp120.dll',
+ '<(PRODUCT_DIR)/msvcr120d.dll',
+ '<(PRODUCT_DIR)/msvcr120.dll',
+ ],
+ },
+ }],
+ ],
+}
diff --git a/chromium/base/base_nacl.gyp b/chromium/base/base_nacl.gyp
new file mode 100644
index 00000000000..5ef664ce5be
--- /dev/null
+++ b/chromium/base/base_nacl.gyp
@@ -0,0 +1,71 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../build/common_untrusted.gypi',
+ 'base.gypi',
+ ],
+ 'conditions': [
+ ['disable_nacl==0 and disable_nacl_untrusted==0', {
+ 'targets': [
+ {
+ 'target_name': 'base_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'base_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_nacl.a',
+ 'build_glibc': 1,
+ 'build_newlib': 1,
+ 'build_irt': 1,
+ 'build_pnacl_newlib': 1,
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'strings/string16.cc',
+ 'sync_socket_nacl.cc',
+ 'time/time_posix.cc',
+ ],
+ 'gcc_compile_flags': [
+ '-fno-strict-aliasing',
+ ],
+ },
+ 'dependencies': [
+ '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+ ],
+ },
+ {
+ 'target_name': 'base_i18n_nacl',
+ 'type': 'none',
+ 'variables': {
+ 'base_i18n_target': 1,
+ 'nacl_untrusted_build': 1,
+ 'nlib_target': 'libbase_i18n_nacl.a',
+ 'build_glibc': 0,
+ 'build_newlib': 1,
+ 'build_irt': 0,
+ 'build_pnacl_newlib': 1,
+ 'sources': [
+ 'base_switches.cc',
+ 'base_switches.h',
+ 'strings/string16.cc',
+ 'sync_socket_nacl.cc',
+ 'time/time_posix.cc',
+ ],
+ },
+ 'dependencies': [
+ '<(DEPTH)/third_party/icu/icu_nacl.gyp:icudata_nacl',
+ '<(DEPTH)/third_party/icu/icu_nacl.gyp:icui18n_nacl',
+ '<(DEPTH)/third_party/icu/icu_nacl.gyp:icuuc_nacl',
+ '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/base/base_paths.cc b/chromium/base/base_paths.cc
index b4fc28b6c75..1b99e850758 100644
--- a/chromium/base/base_paths.cc
+++ b/chromium/base/base_paths.cc
@@ -13,35 +13,34 @@ namespace base {
bool PathProvider(int key, FilePath* result) {
// NOTE: DIR_CURRENT is a special case in PathService::Get
- FilePath cur;
switch (key) {
case DIR_EXE:
- PathService::Get(FILE_EXE, &cur);
- cur = cur.DirName();
- break;
+ PathService::Get(FILE_EXE, result);
+ *result = result->DirName();
+ return true;
case DIR_MODULE:
- PathService::Get(FILE_MODULE, &cur);
- cur = cur.DirName();
- break;
+ PathService::Get(FILE_MODULE, result);
+ *result = result->DirName();
+ return true;
case DIR_TEMP:
- if (!base::GetTempDir(&cur))
+ if (!GetTempDir(result))
return false;
- break;
+ return true;
+ case base::DIR_HOME:
+ *result = GetHomeDir();
+ return true;
case DIR_TEST_DATA:
- if (!PathService::Get(DIR_SOURCE_ROOT, &cur))
+ if (!PathService::Get(DIR_SOURCE_ROOT, result))
return false;
- cur = cur.Append(FILE_PATH_LITERAL("base"));
- cur = cur.Append(FILE_PATH_LITERAL("test"));
- cur = cur.Append(FILE_PATH_LITERAL("data"));
- if (!base::PathExists(cur)) // We don't want to create this.
+ *result = result->Append(FILE_PATH_LITERAL("base"));
+ *result = result->Append(FILE_PATH_LITERAL("test"));
+ *result = result->Append(FILE_PATH_LITERAL("data"));
+ if (!PathExists(*result)) // We don't want to create this.
return false;
- break;
+ return true;
default:
return false;
}
-
- *result = cur;
- return true;
}
} // namespace base
diff --git a/chromium/base/base_paths.h b/chromium/base/base_paths.h
index f9601a290ae..26b2fd4c9a1 100644
--- a/chromium/base/base_paths.h
+++ b/chromium/base/base_paths.h
@@ -31,6 +31,10 @@ enum BasePathKey {
DIR_EXE, // Directory containing FILE_EXE.
DIR_MODULE, // Directory containing FILE_MODULE.
DIR_TEMP, // Temporary directory.
+ DIR_HOME, // User's root home directory. On Windows this will look
+ // like "C:\Users\you" (or on XP
+ // "C:\Document and Settings\you") which isn't necessarily
+ // a great place to put files.
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
diff --git a/chromium/base/base_paths_android.cc b/chromium/base/base_paths_android.cc
index 68cc5a72cef..27be24ea982 100644
--- a/chromium/base/base_paths_android.cc
+++ b/chromium/base/base_paths_android.cc
@@ -47,9 +47,6 @@ bool PathProviderAndroid(int key, FilePath* result) {
return base::android::GetCacheDirectory(result);
case base::DIR_ANDROID_APP_DATA:
return base::android::GetDataDirectory(result);
- case base::DIR_HOME:
- *result = GetHomeDir();
- return true;
case base::DIR_ANDROID_EXTERNAL_STORAGE:
return base::android::GetExternalStorageDirectory(result);
default:
diff --git a/chromium/base/base_paths_mac.mm b/chromium/base/base_paths_mac.mm
index 86d6a80e9ad..3930c44c3b7 100644
--- a/chromium/base/base_paths_mac.mm
+++ b/chromium/base/base_paths_mac.mm
@@ -106,9 +106,6 @@ bool PathProviderMac(int key, base::FilePath* 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;
}
diff --git a/chromium/base/base_paths_posix.cc b/chromium/base/base_paths_posix.cc
index b4147e99327..cf136d47fa3 100644
--- a/chromium/base/base_paths_posix.cc
+++ b/chromium/base/base_paths_posix.cc
@@ -109,9 +109,6 @@ bool PathProviderPosix(int key, FilePath* result) {
*result = cache_dir;
return true;
}
- case base::DIR_HOME:
- *result = GetHomeDir();
- return true;
}
return false;
}
diff --git a/chromium/base/base_paths_posix.h b/chromium/base/base_paths_posix.h
index 811c8cb8948..ef002aeb0bc 100644
--- a/chromium/base/base_paths_posix.h
+++ b/chromium/base/base_paths_posix.h
@@ -19,8 +19,6 @@ enum {
// 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
};
diff --git a/chromium/base/base_paths_win.cc b/chromium/base/base_paths_win.cc
index bc883358add..509d5fd7ef1 100644
--- a/chromium/base/base_paths_win.cc
+++ b/chromium/base/base_paths_win.cc
@@ -127,12 +127,6 @@ bool PathProviderWin(int key, FilePath* result) {
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;
@@ -197,6 +191,13 @@ bool PathProviderWin(int key, FilePath* result) {
cur = cur.AppendASCII("User Pinned");
cur = cur.AppendASCII("TaskBar");
break;
+ case base::DIR_WINDOWS_FONTS:
+ if (FAILED(SHGetFolderPath(
+ NULL, CSIDL_FONTS, NULL, SHGFP_TYPE_CURRENT, system_buffer))) {
+ return false;
+ }
+ cur = FilePath(system_buffer);
+ break;
default:
return false;
}
diff --git a/chromium/base/base_paths_win.h b/chromium/base/base_paths_win.h
index 11bc111d66c..b042d087379 100644
--- a/chromium/base/base_paths_win.h
+++ b/chromium/base/base_paths_win.h
@@ -25,7 +25,6 @@ enum {
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.
@@ -42,6 +41,7 @@ enum {
// of the Default user.
DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar via
// base::win::TaskbarPinShortcutLink().
+ DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts.
PATH_WIN_END
};
diff --git a/chromium/base/base_switches.cc b/chromium/base/base_switches.cc
index 91e401ca497..81698cdfc13 100644
--- a/chromium/base/base_switches.cc
+++ b/chromium/base/base_switches.cc
@@ -6,12 +6,6 @@
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";
@@ -20,9 +14,6 @@ const char kDisableBreakpad[] = "disable-breakpad";
// generated internally.
const char kEnableCrashReporter[] = "enable-crash-reporter";
-// Enable DCHECKs in release mode.
-const char kEnableDCHECK[] = "enable-dcheck";
-
// Generates full memory crash dump.
const char kFullMemoryCrashReport[] = "full-memory-crash-report";
diff --git a/chromium/base/base_switches.h b/chromium/base/base_switches.h
index b679e6d9ebf..6e391a45fcf 100644
--- a/chromium/base/base_switches.h
+++ b/chromium/base/base_switches.h
@@ -11,10 +11,8 @@
namespace switches {
-extern const char kDebugOnStart[];
extern const char kDisableBreakpad[];
extern const char kEnableCrashReporter[];
-extern const char kEnableDCHECK[];
extern const char kFullMemoryCrashReport[];
extern const char kNoErrorDialogs[];
extern const char kProfilerTiming[];
diff --git a/chromium/base/base_unittests.isolate b/chromium/base/base_unittests.isolate
index 166cf801eca..b5464be8e37 100644
--- a/chromium/base/base_unittests.isolate
+++ b/chromium/base/base_unittests.isolate
@@ -32,9 +32,7 @@
'../testing/test_env.py',
'<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
],
- 'isolate_dependency_untracked': [
- '../tools/swarming_client/',
- ],
+ 'read_only': 1,
},
}],
['OS=="mac" or OS=="win"', {
@@ -50,9 +48,11 @@
['OS=="win"', {
'variables': {
'isolate_dependency_tracked': [
- '<(PRODUCT_DIR)/icudt.dll',
],
},
}],
],
+ 'includes': [
+ 'base.isolate',
+ ],
}
diff --git a/chromium/base/base_untrusted.gyp b/chromium/base/base_untrusted.gyp
deleted file mode 100644
index e4b005bc291..00000000000
--- a/chromium/base/base_untrusted.gyp
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (c) 2012 The Chromium 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,
- 'build_irt': 1,
- 'sources': [
- 'base_switches.cc',
- 'base_switches.h',
- 'strings/string16.cc',
- 'sync_socket_nacl.cc',
- 'time/time_posix.cc',
- ],
- },
- 'dependencies': [
- '<(DEPTH)/native_client/tools.gyp:prep_toolchain',
- ],
- },
- ],
- }],
- ],
-}
diff --git a/chromium/base/basictypes.h b/chromium/base/basictypes.h
index e77d7b10f24..b0019f2a193 100644
--- a/chromium/base/basictypes.h
+++ b/chromium/base/basictypes.h
@@ -1,377 +1,58 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Copyright 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 file contains definitions of our old basic integral types
+// ((u)int{8,16,32,64}) and further includes. I recommend that you use the C99
+// standard types instead, and include <stdint.h>/<stddef.h>/etc. as needed.
+// Note that the macros and macro-like constructs that were formerly defined in
+// this file are now available separately in base/macros.h.
+
#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/compiler_specific.h"
-#include "base/port.h" // Types that only need exist on certain systems
+#include <limits.h> // So we can set the bounds of our types.
+#include <stddef.h> // For size_t.
+#include <stdint.h> // For intptr_t.
-#ifndef COMPILER_MSVC
-// stdint.h is part of C99 but MSVC doesn't have it.
-#include <stdint.h> // For intptr_t.
-#endif
+#include "base/macros.h"
+#include "base/port.h" // Types that only need exist on certain systems.
-typedef signed char schar;
-typedef signed char int8;
-typedef short int16;
-typedef int int32;
+// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>).
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+// TODO(vtl): Figure what's up with the 64-bit types. Can we just define them as
+// |int64_t|/|uint64_t|?
// 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: It is DANGEROUS to compare signed with unsigned types in loop
-// conditions and other conditional expressions, and it is DANGEROUS to
-// compute object/allocation sizes, indices, and offsets with signed types.
-// Integer overflow behavior for signed types is UNDEFINED in the C/C++
-// standards, but is defined for unsigned types.
-//
-// Use the unsigned types if your variable represents a bit pattern (e.g. a
-// hash value), object or allocation size, object count, offset,
-// array/vector index, etc.
-//
-// Do NOT use 'unsigned' to express "this value should always be positive";
-// use assertions for this.
-//
-// See the Chromium style guide for more information.
-// https://sites.google.com/a/chromium.org/dev/developers/coding-style
-
-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 long int64;
typedef unsigned long uint64;
#else
+typedef long long int64;
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;
-
+// DEPRECATED: Please use std::numeric_limits (from <limits>) instead.
const uint8 kuint8max = (( uint8) 0xFF);
const uint16 kuint16max = ((uint16) 0xFFFF);
const uint32 kuint32max = ((uint32) 0xFFFFFFFF);
-const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF));
+const uint64 kuint64max = ((uint64) 0xFFFFFFFFFFFFFFFFULL);
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));
-
-// Put this in the private: declarations for a class to be uncopyable.
-#define DISALLOW_COPY(TypeName) \
- TypeName(const TypeName&)
-
-// Put this in the private: declarations for a class to be unassignable.
-#define DISALLOW_ASSIGN(TypeName) \
- void operator=(const TypeName&)
-
-// 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 banned 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
-
-#define arraysize(array) (sizeof(ArraySizeHelper(array)))
-
-// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
-// but can be used on anonymous types or types defined inside
-// functions. It's less safe than arraysize as it accepts some
-// (although not all) pointers. Therefore, you should use arraysize
-// whenever possible.
-//
-// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
-// size_t.
-//
-// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
-//
-// "warning: division by zero in ..."
-//
-// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
-// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
-//
-// The following comments are on the implementation details, and can
-// be ignored by the users.
-//
-// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
-// the array) and sizeof(*(arr)) (the # of bytes in one array
-// element). If the former is divisible by the latter, perhaps arr is
-// indeed an array, in which case the division result is the # of
-// elements in the array. Otherwise, arr cannot possibly be an array,
-// and we generate a compiler error to prevent the code from
-// compiling.
-//
-// Since the size of bool is implementation-defined, we need to cast
-// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
-// result has type size_t.
-//
-// This macro is not perfect as it wrongfully accepts certain
-// pointers, namely where the pointer size is divisible by the pointee
-// size. Since all our code has to go through a 32-bit compiler,
-// where a pointer is 4 bytes, this means all pointers to a type whose
-// size is 3 or greater than 4 will be (righteously) rejected.
-
-#define ARRAYSIZE_UNSAFE(a) \
- ((sizeof(a) / sizeof(*(a))) / \
- static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
-
-
-// Use implicit_cast as a safe version of static_cast or const_cast
-// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
-// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
-// a const pointer to Foo).
-// When you use implicit_cast, the compiler checks that the cast is safe.
-// Such explicit implicit_casts are necessary in surprisingly many
-// situations where C++ demands an exact type match instead of an
-// argument type convertible 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.
-
-#undef COMPILE_ASSERT
-
-#if __cplusplus >= 201103L
-
-// Under C++11, just use static_assert.
-#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
-
-#else
-
-template <bool>
-struct CompileAssert {
-};
-
-#define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ALLOW_UNUSED
-
-// 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.
-
-#endif
-
-// 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 between 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_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
-
- 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
+const int64 kint64min = (( int64) 0x8000000000000000LL);
+const int64 kint64max = (( int64) 0x7FFFFFFFFFFFFFFFLL);
#endif // BASE_BASICTYPES_H_
diff --git a/chromium/base/big_endian.cc b/chromium/base/big_endian.cc
new file mode 100644
index 00000000000..fd9e7600ac1
--- /dev/null
+++ b/chromium/base/big_endian.cc
@@ -0,0 +1,97 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/big_endian.h"
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+BigEndianReader::BigEndianReader(const char* buf, size_t len)
+ : ptr_(buf), end_(ptr_ + len) {}
+
+bool BigEndianReader::Skip(size_t len) {
+ if (ptr_ + len > end_)
+ return false;
+ ptr_ += len;
+ return true;
+}
+
+bool BigEndianReader::ReadBytes(void* out, size_t len) {
+ if (ptr_ + len > end_)
+ return false;
+ memcpy(out, ptr_, len);
+ ptr_ += len;
+ return true;
+}
+
+bool BigEndianReader::ReadPiece(base::StringPiece* out, size_t len) {
+ if (ptr_ + len > end_)
+ return false;
+ *out = base::StringPiece(ptr_, len);
+ ptr_ += len;
+ return true;
+}
+
+template<typename T>
+bool BigEndianReader::Read(T* value) {
+ if (ptr_ + sizeof(T) > end_)
+ return false;
+ ReadBigEndian<T>(ptr_, value);
+ ptr_ += sizeof(T);
+ return true;
+}
+
+bool BigEndianReader::ReadU8(uint8* value) {
+ return Read(value);
+}
+
+bool BigEndianReader::ReadU16(uint16* value) {
+ return Read(value);
+}
+
+bool BigEndianReader::ReadU32(uint32* value) {
+ return Read(value);
+}
+
+BigEndianWriter::BigEndianWriter(char* buf, size_t len)
+ : ptr_(buf), end_(ptr_ + len) {}
+
+bool BigEndianWriter::Skip(size_t len) {
+ if (ptr_ + len > end_)
+ return false;
+ ptr_ += len;
+ return true;
+}
+
+bool BigEndianWriter::WriteBytes(const void* buf, size_t len) {
+ if (ptr_ + len > end_)
+ return false;
+ memcpy(ptr_, buf, len);
+ ptr_ += len;
+ return true;
+}
+
+template<typename T>
+bool BigEndianWriter::Write(T value) {
+ if (ptr_ + sizeof(T) > end_)
+ return false;
+ WriteBigEndian<T>(ptr_, value);
+ ptr_ += sizeof(T);
+ return true;
+}
+
+bool BigEndianWriter::WriteU8(uint8 value) {
+ return Write(value);
+}
+
+bool BigEndianWriter::WriteU16(uint16 value) {
+ return Write(value);
+}
+
+bool BigEndianWriter::WriteU32(uint32 value) {
+ return Write(value);
+}
+
+} // namespace base
diff --git a/chromium/base/big_endian.h b/chromium/base/big_endian.h
new file mode 100644
index 00000000000..35df5e79776
--- /dev/null
+++ b/chromium/base/big_endian.h
@@ -0,0 +1,102 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_BIG_ENDIAN_H_
+#define BASE_BIG_ENDIAN_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/strings/string_piece.h"
+
+namespace base {
+
+// Read an integer (signed or unsigned) from |buf| in Big Endian order.
+// Note: this loop is unrolled with -O1 and above.
+// NOTE(szym): glibc dns-canon.c and SpdyFrameBuilder use
+// ntohs(*(uint16_t*)ptr) which is potentially unaligned.
+// This would cause SIGBUS on ARMv5 or earlier and ARMv6-M.
+template<typename T>
+inline void ReadBigEndian(const char buf[], T* out) {
+ *out = buf[0];
+ for (size_t i = 1; i < sizeof(T); ++i) {
+ *out <<= 8;
+ // Must cast to uint8 to avoid clobbering by sign extension.
+ *out |= static_cast<uint8>(buf[i]);
+ }
+}
+
+// Write an integer (signed or unsigned) |val| to |buf| in Big Endian order.
+// Note: this loop is unrolled with -O1 and above.
+template<typename T>
+inline void WriteBigEndian(char buf[], T val) {
+ for (size_t i = 0; i < sizeof(T); ++i) {
+ buf[sizeof(T)-i-1] = static_cast<char>(val & 0xFF);
+ val >>= 8;
+ }
+}
+
+// Specializations to make clang happy about the (dead code) shifts above.
+template<>
+inline void ReadBigEndian<uint8>(const char buf[], uint8* out) {
+ *out = buf[0];
+}
+
+template<>
+inline void WriteBigEndian<uint8>(char buf[], uint8 val) {
+ buf[0] = static_cast<char>(val);
+}
+
+// Allows reading integers in network order (big endian) while iterating over
+// an underlying buffer. All the reading functions advance the internal pointer.
+class BASE_EXPORT BigEndianReader {
+ public:
+ BigEndianReader(const char* buf, size_t len);
+
+ const char* ptr() const { return ptr_; }
+ int remaining() const { return end_ - ptr_; }
+
+ bool Skip(size_t len);
+ bool ReadBytes(void* out, size_t len);
+ // Creates a StringPiece in |out| that points to the underlying buffer.
+ bool ReadPiece(base::StringPiece* out, size_t len);
+ bool ReadU8(uint8* value);
+ bool ReadU16(uint16* value);
+ bool ReadU32(uint32* value);
+
+ private:
+ // Hidden to promote type safety.
+ template<typename T>
+ bool Read(T* v);
+
+ const char* ptr_;
+ const char* end_;
+};
+
+// Allows writing integers in network order (big endian) while iterating over
+// an underlying buffer. All the writing functions advance the internal pointer.
+class BASE_EXPORT BigEndianWriter {
+ public:
+ BigEndianWriter(char* buf, size_t len);
+
+ char* ptr() const { return ptr_; }
+ int remaining() const { return end_ - ptr_; }
+
+ bool Skip(size_t len);
+ bool WriteBytes(const void* buf, size_t len);
+ bool WriteU8(uint8 value);
+ bool WriteU16(uint16 value);
+ bool WriteU32(uint32 value);
+
+ private:
+ // Hidden to promote type safety.
+ template<typename T>
+ bool Write(T v);
+
+ char* ptr_;
+ char* end_;
+};
+
+} // namespace base
+
+#endif // BASE_BIG_ENDIAN_H_
diff --git a/chromium/base/big_endian_unittest.cc b/chromium/base/big_endian_unittest.cc
new file mode 100644
index 00000000000..b57aeb29ac9
--- /dev/null
+++ b/chromium/base/big_endian_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/big_endian.h"
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(BigEndianReaderTest, ReadsValues) {
+ char data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC };
+ char buf[2];
+ uint8 u8;
+ uint16 u16;
+ uint32 u32;
+ base::StringPiece piece;
+ BigEndianReader reader(data, sizeof(data));
+
+ EXPECT_TRUE(reader.Skip(2));
+ EXPECT_EQ(data + 2, reader.ptr());
+ EXPECT_EQ(reader.remaining(), static_cast<int>(sizeof(data)) - 2);
+ EXPECT_TRUE(reader.ReadBytes(buf, sizeof(buf)));
+ EXPECT_EQ(0x2, buf[0]);
+ EXPECT_EQ(0x3, buf[1]);
+ EXPECT_TRUE(reader.ReadU8(&u8));
+ EXPECT_EQ(0x4, u8);
+ EXPECT_TRUE(reader.ReadU16(&u16));
+ EXPECT_EQ(0x0506, u16);
+ EXPECT_TRUE(reader.ReadU32(&u32));
+ EXPECT_EQ(0x0708090Au, u32);
+ base::StringPiece expected(reader.ptr(), 2);
+ EXPECT_TRUE(reader.ReadPiece(&piece, 2));
+ EXPECT_EQ(2u, piece.size());
+ EXPECT_EQ(expected.data(), piece.data());
+}
+
+TEST(BigEndianReaderTest, RespectsLength) {
+ char data[4];
+ char buf[2];
+ uint8 u8;
+ uint16 u16;
+ uint32 u32;
+ base::StringPiece piece;
+ BigEndianReader reader(data, sizeof(data));
+ // 4 left
+ EXPECT_FALSE(reader.Skip(6));
+ EXPECT_TRUE(reader.Skip(1));
+ // 3 left
+ EXPECT_FALSE(reader.ReadU32(&u32));
+ EXPECT_FALSE(reader.ReadPiece(&piece, 4));
+ EXPECT_TRUE(reader.Skip(2));
+ // 1 left
+ EXPECT_FALSE(reader.ReadU16(&u16));
+ EXPECT_FALSE(reader.ReadBytes(buf, 2));
+ EXPECT_TRUE(reader.Skip(1));
+ // 0 left
+ EXPECT_FALSE(reader.ReadU8(&u8));
+ EXPECT_EQ(0, reader.remaining());
+}
+
+TEST(BigEndianWriterTest, WritesValues) {
+ char expected[] = { 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 0xA };
+ char data[sizeof(expected)];
+ char buf[] = { 0x2, 0x3 };
+ memset(data, 0, sizeof(data));
+ BigEndianWriter writer(data, sizeof(data));
+
+ EXPECT_TRUE(writer.Skip(2));
+ EXPECT_TRUE(writer.WriteBytes(buf, sizeof(buf)));
+ EXPECT_TRUE(writer.WriteU8(0x4));
+ EXPECT_TRUE(writer.WriteU16(0x0506));
+ EXPECT_TRUE(writer.WriteU32(0x0708090A));
+ EXPECT_EQ(0, memcmp(expected, data, sizeof(expected)));
+}
+
+TEST(BigEndianWriterTest, RespectsLength) {
+ char data[4];
+ char buf[2];
+ uint8 u8 = 0;
+ uint16 u16 = 0;
+ uint32 u32 = 0;
+ BigEndianWriter writer(data, sizeof(data));
+ // 4 left
+ EXPECT_FALSE(writer.Skip(6));
+ EXPECT_TRUE(writer.Skip(1));
+ // 3 left
+ EXPECT_FALSE(writer.WriteU32(u32));
+ EXPECT_TRUE(writer.Skip(2));
+ // 1 left
+ EXPECT_FALSE(writer.WriteU16(u16));
+ EXPECT_FALSE(writer.WriteBytes(buf, 2));
+ EXPECT_TRUE(writer.Skip(1));
+ // 0 left
+ EXPECT_FALSE(writer.WriteU8(u8));
+ EXPECT_EQ(0, writer.remaining());
+}
+
+} // namespace base
diff --git a/chromium/base/bind.h b/chromium/base/bind.h
index 5cf124df32f..b14f70c109f 100644
--- a/chromium/base/bind.h
+++ b/chromium/base/bind.h
@@ -65,12 +65,6 @@ Bind(Functor 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;
diff --git a/chromium/base/bind.h.pump b/chromium/base/bind.h.pump
index b321649aea6..2cdd7d5e498 100644
--- a/chromium/base/bind.h.pump
+++ b/chromium/base/bind.h.pump
@@ -93,14 +93,14 @@ $if ARITY > 0 [[, ]] $for ARG , [[const P$(ARG)& p$(ARG)]]) {
typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
typedef typename internal::FunctorTraits<Functor>::RunType RunType;
+$if ARITY > 0 [[
+
// 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
diff --git a/chromium/base/bind_helpers.h b/chromium/base/bind_helpers.h
index d717892c8f5..e31ef05ff1a 100644
--- a/chromium/base/bind_helpers.h
+++ b/chromium/base/bind_helpers.h
@@ -12,7 +12,7 @@
//
// ARGUMENT BINDING WRAPPERS
//
-// The wrapper functions are base::Unretained(), base::Owned(), bass::Passed(),
+// The wrapper functions are base::Unretained(), base::Owned(), base::Passed(),
// base::ConstRef(), and base::IgnoreResult().
//
// Unretained() allows Bind() to bind a non-refcounted class, and to disable
diff --git a/chromium/base/bind_unittest.cc b/chromium/base/bind_unittest.cc
index 2c93d53ee4b..e1f15cb2011 100644
--- a/chromium/base/bind_unittest.cc
+++ b/chromium/base/bind_unittest.cc
@@ -127,10 +127,6 @@ class CopyCounter {
return *copies_;
}
- int assigns() const {
- return *assigns_;
- }
-
private:
int* copies_;
int* assigns_;
diff --git a/chromium/base/bind_unittest.nc b/chromium/base/bind_unittest.nc
index 033acfaa057..de81646fbff 100644
--- a/chromium/base/bind_unittest.nc
+++ b/chromium/base/bind_unittest.nc
@@ -9,8 +9,7 @@
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.
+// helper function declarations will generate unused definition warnings.
static const int kParentValue = 1;
static const int kChildValue = 2;
diff --git a/chromium/base/callback_list.h b/chromium/base/callback_list.h
index d12a1e95baa..5b911fd4867 100644
--- a/chromium/base/callback_list.h
+++ b/chromium/base/callback_list.h
@@ -89,10 +89,13 @@ class CallbackListBase {
}
~Subscription() {
- if (list_->active_iterator_count_)
+ if (list_->active_iterator_count_) {
iter_->Reset();
- else
+ } else {
list_->callbacks_.erase(iter_);
+ if (!list_->removal_callback_.is_null())
+ list_->removal_callback_.Run();
+ }
}
private:
@@ -111,6 +114,18 @@ class CallbackListBase {
new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
}
+ // Sets a callback which will be run when a subscription list is changed.
+ void set_removal_callback(const Closure& callback) {
+ removal_callback_ = callback;
+ }
+
+ // Returns true if there are no subscriptions. This is only valid to call when
+ // not looping through the list.
+ bool empty() {
+ DCHECK_EQ(0, active_iterator_count_);
+ return callbacks_.empty();
+ }
+
protected:
// An iterator class that can be used to access the list of callbacks.
class Iterator {
@@ -167,17 +182,24 @@ class CallbackListBase {
// iteration.
void Compact() {
typename std::list<CallbackType>::iterator it = callbacks_.begin();
+ bool updated = false;
while (it != callbacks_.end()) {
- if ((*it).is_null())
+ if ((*it).is_null()) {
+ updated = true;
it = callbacks_.erase(it);
- else
+ } else {
++it;
+ }
+
+ if (updated && !removal_callback_.is_null())
+ removal_callback_.Run();
}
}
private:
std::list<CallbackType> callbacks_;
int active_iterator_count_;
+ Closure removal_callback_;
DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
};
diff --git a/chromium/base/callback_list.h.pump b/chromium/base/callback_list.h.pump
index ea2103ebeef..d7f84736c15 100644
--- a/chromium/base/callback_list.h.pump
+++ b/chromium/base/callback_list.h.pump
@@ -94,10 +94,13 @@ class CallbackListBase {
}
~Subscription() {
- if (list_->active_iterator_count_)
+ if (list_->active_iterator_count_) {
iter_->Reset();
- else
+ } else {
list_->callbacks_.erase(iter_);
+ if (!list_->removal_callback_.is_null())
+ list_->removal_callback_.Run();
+ }
}
private:
@@ -116,6 +119,18 @@ class CallbackListBase {
new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
}
+ // Sets a callback which will be run when a subscription list is changed.
+ void set_removal_callback(const Closure& callback) {
+ removal_callback_ = callback;
+ }
+
+ // Returns true if there are no subscriptions. This is only valid to call when
+ // not looping through the list.
+ bool empty() {
+ DCHECK_EQ(0, active_iterator_count_);
+ return callbacks_.empty();
+ }
+
protected:
// An iterator class that can be used to access the list of callbacks.
class Iterator {
@@ -172,17 +187,24 @@ class CallbackListBase {
// iteration.
void Compact() {
typename std::list<CallbackType>::iterator it = callbacks_.begin();
+ bool updated = false;
while (it != callbacks_.end()) {
- if ((*it).is_null())
+ if ((*it).is_null()) {
+ updated = true;
it = callbacks_.erase(it);
- else
+ } else {
++it;
+ }
+
+ if (updated && !removal_callback_.is_null())
+ removal_callback_.Run();
}
}
private:
std::list<CallbackType> callbacks_;
int active_iterator_count_;
+ Closure removal_callback_;
DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
};
diff --git a/chromium/base/cancelable_callback_unittest.cc b/chromium/base/cancelable_callback_unittest.cc
index 89c603c9537..fcbe23c1de3 100644
--- a/chromium/base/cancelable_callback_unittest.cc
+++ b/chromium/base/cancelable_callback_unittest.cc
@@ -159,7 +159,7 @@ TEST(CancelableCallbackTest, IsNull) {
// CancelableCallback posted to a MessageLoop with PostTask.
// - Callbacks posted to a MessageLoop can be cancelled.
TEST(CancelableCallbackTest, PostTask) {
- MessageLoop loop(MessageLoop::TYPE_DEFAULT);
+ MessageLoop loop;
int count = 0;
CancelableClosure cancelable(base::Bind(&Increment,
diff --git a/chromium/base/command_line.cc b/chromium/base/command_line.cc
index e00eee6bd89..6e37d6bf72b 100644
--- a/chromium/base/command_line.cc
+++ b/chromium/base/command_line.cc
@@ -20,11 +20,12 @@
#include <shellapi.h>
#endif
-using base::FilePath;
+namespace base {
CommandLine* CommandLine::current_process_commandline_ = NULL;
namespace {
+
const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
@@ -81,7 +82,8 @@ void AppendSwitchesAndArguments(CommandLine& command_line,
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);
+ command_line.AppendSwitchNative(UTF16ToASCII(switch_string),
+ switch_value);
#elif defined(OS_POSIX)
command_line.AppendSwitchNative(switch_string, switch_value);
#endif
@@ -308,7 +310,7 @@ std::string CommandLine::GetSwitchValueASCII(
return std::string();
}
#if defined(OS_WIN)
- return WideToASCII(value);
+ return UTF16ToASCII(value);
#else
return value;
#endif
@@ -413,7 +415,7 @@ void CommandLine::PrependWrapper(const CommandLine::StringType& wrapper) {
// The wrapper may have embedded arguments (like "gdb --args"). In this case,
// we don't pretend to do anything fancy, we just split on spaces.
StringVector wrapper_argv;
- base::SplitString(wrapper, FILE_PATH_LITERAL(' '), &wrapper_argv);
+ 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();
@@ -431,8 +433,10 @@ void CommandLine::ParseFromString(const std::wstring& command_line) {
args = ::CommandLineToArgvW(command_line_string.c_str(), &num_args);
DPLOG_IF(FATAL, !args) << "CommandLineToArgvW failed on command line: "
- << command_line;
+ << UTF16ToUTF8(command_line);
InitFromArgv(num_args, args);
LocalFree(args);
}
#endif
+
+} // namespace base
diff --git a/chromium/base/command_line.h b/chromium/base/command_line.h
index 81bd4b73761..fd06e61c774 100644
--- a/chromium/base/command_line.h
+++ b/chromium/base/command_line.h
@@ -24,8 +24,8 @@
#include "build/build_config.h"
namespace base {
+
class FilePath;
-}
class BASE_EXPORT CommandLine {
public:
@@ -45,7 +45,7 @@ class BASE_EXPORT CommandLine {
explicit CommandLine(NoProgram no_program);
// Construct a new command line with |program| as argv[0].
- explicit CommandLine(const base::FilePath& program);
+ explicit CommandLine(const FilePath& program);
// Construct a new command line from an argument list.
CommandLine(int argc, const CharType* const* argv);
@@ -108,8 +108,8 @@ class BASE_EXPORT CommandLine {
const StringVector& argv() const { return argv_; }
// Get and Set the program part of the command line string (the first item).
- base::FilePath GetProgram() const;
- void SetProgram(const base::FilePath& program);
+ FilePath GetProgram() const;
+ void SetProgram(const FilePath& program);
// Returns true if this command line contains the given switch.
// (Switch names are case-insensitive).
@@ -118,7 +118,7 @@ class BASE_EXPORT CommandLine {
// 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;
- base::FilePath GetSwitchValuePath(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.
@@ -128,7 +128,7 @@ class BASE_EXPORT CommandLine {
// Note: Switches will precede arguments regardless of appending order.
void AppendSwitch(const std::string& switch_string);
void AppendSwitchPath(const std::string& switch_string,
- const base::FilePath& path);
+ const FilePath& path);
void AppendSwitchNative(const std::string& switch_string,
const StringType& value);
void AppendSwitchASCII(const std::string& switch_string,
@@ -148,7 +148,7 @@ class BASE_EXPORT CommandLine {
// 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 base::FilePath& value);
+ void AppendArgPath(const FilePath& value);
void AppendArgNative(const StringType& value);
// Append the switches and arguments from another command line to this one.
@@ -186,4 +186,9 @@ class BASE_EXPORT CommandLine {
size_t begin_args_;
};
+} // namespace base
+
+// TODO(brettw) remove once all callers specify the namespace properly.
+using base::CommandLine;
+
#endif // BASE_COMMAND_LINE_H_
diff --git a/chromium/base/command_line_unittest.cc b/chromium/base/command_line_unittest.cc
index 2c947fc39ad..06c5006505a 100644
--- a/chromium/base/command_line_unittest.cc
+++ b/chromium/base/command_line_unittest.cc
@@ -198,10 +198,14 @@ TEST(CommandLineTest, GetArgumentsString) {
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));
+ CommandLine::StringType expected_first_arg(
+ base::UTF8ToUTF16(kFirstArgName));
+ CommandLine::StringType expected_second_arg(
+ base::UTF8ToUTF16(kSecondArgName));
+ CommandLine::StringType expected_third_arg(
+ base::UTF8ToUTF16(kThirdArgName));
+ CommandLine::StringType expected_fourth_arg(
+ base::UTF8ToUTF16(kFourthArgName));
#elif defined(OS_POSIX)
CommandLine::StringType expected_first_arg(kFirstArgName);
CommandLine::StringType expected_second_arg(kSecondArgName);
diff --git a/chromium/base/compiler_specific.h b/chromium/base/compiler_specific.h
index dc4b2334987..9e2111db6b6 100644
--- a/chromium/base/compiler_specific.h
+++ b/chromium/base/compiler_specific.h
@@ -137,9 +137,7 @@
// method in the parent class.
// Use like:
// virtual void foo() OVERRIDE;
-#if defined(COMPILER_MSVC)
-#define OVERRIDE override
-#elif defined(__clang__)
+#if defined(__clang__) || defined(COMPILER_MSVC)
#define OVERRIDE override
#elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700
@@ -154,10 +152,7 @@
// Use like:
// virtual void foo() FINAL;
// class B FINAL : public A {};
-#if defined(COMPILER_MSVC)
-// TODO(jered): Change this to "final" when chromium no longer uses MSVC 2010.
-#define FINAL sealed
-#elif defined(__clang__)
+#if defined(__clang__) || defined(COMPILER_MSVC)
#define FINAL final
#elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \
(__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700
@@ -196,12 +191,9 @@
// If available, it would look like:
// __attribute__((format(wprintf, format_param, dots_param)))
-
// MemorySanitizer annotations.
-#ifdef MEMORY_SANITIZER
-extern "C" {
-void __msan_unpoison(const void *p, unsigned long s);
-} // extern "C"
+#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
+#include <sanitizer/msan_interface.h>
// Mark a memory region fully initialized.
// Use this to annotate code that deliberately reads uninitialized data, for
@@ -211,4 +203,22 @@ void __msan_unpoison(const void *p, unsigned long s);
#define MSAN_UNPOISON(p, s)
#endif // MEMORY_SANITIZER
+// Macro useful for writing cross-platform function pointers.
+#if !defined(CDECL)
+#if defined(OS_WIN)
+#define CDECL __cdecl
+#else // defined(OS_WIN)
+#define CDECL
+#endif // defined(OS_WIN)
+#endif // !defined(CDECL)
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(UNLIKELY)
+
#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/chromium/base/containers/hash_tables.h b/chromium/base/containers/hash_tables.h
index 365b586ba93..6f37c49c3e4 100644
--- a/chromium/base/containers/hash_tables.h
+++ b/chromium/base/containers/hash_tables.h
@@ -140,7 +140,7 @@ inline std::size_t HashInts32(uint32 value1, uint32 value2) {
hash64 = hash64 * odd_random + shift_random;
std::size_t high_bits = static_cast<std::size_t>(
- hash64 >> (sizeof(uint64) - sizeof(std::size_t)));
+ hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t))));
return high_bits;
}
@@ -175,7 +175,7 @@ inline std::size_t HashInts64(uint64 value1, uint64 value2) {
hash64 = hash64 * odd_random + shift_random;
std::size_t high_bits = static_cast<std::size_t>(
- hash64 >> (sizeof(uint64) - sizeof(std::size_t)));
+ hash64 >> (8 * (sizeof(uint64) - sizeof(std::size_t))));
return high_bits;
}
diff --git a/chromium/base/containers/scoped_ptr_hash_map.h b/chromium/base/containers/scoped_ptr_hash_map.h
index b636e604a5a..dedf21365be 100644
--- a/chromium/base/containers/scoped_ptr_hash_map.h
+++ b/chromium/base/containers/scoped_ptr_hash_map.h
@@ -24,6 +24,9 @@ class ScopedPtrHashMap {
typedef base::hash_map<Key, Value*> Container;
public:
+ typedef typename Container::key_type key_type;
+ typedef typename Container::mapped_type mapped_type;
+ typedef typename Container::value_type value_type;
typedef typename Container::iterator iterator;
typedef typename Container::const_iterator const_iterator;
@@ -35,30 +38,29 @@ class ScopedPtrHashMap {
data_.swap(other.data_);
}
- std::pair<iterator, bool> insert(
- std::pair<Key, const scoped_ptr<Value> > pair) {
- return data_.insert(
- std::pair<Key, Value*>(pair.first, pair.second.release()));
- }
-
// Replaces value but not key if key is already present.
- std::pair<iterator, bool> set(Key key, scoped_ptr<Value> data) {
+ iterator set(const Key& key, scoped_ptr<Value> data) {
iterator it = find(key);
- if (it != end())
- erase(it);
- Value* raw_ptr = data.release();
- return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
+ if (it != end()) {
+ delete it->second;
+ it->second = data.release();
+ return it;
+ }
+
+ return data_.insert(std::make_pair(key, data.release())).first;
}
// Does nothing if key is already present
- std::pair<iterator, bool> add(Key key, scoped_ptr<Value> data) {
- Value* raw_ptr = data.release();
- return data_.insert(std::pair<Key, Value*>(key, raw_ptr));
+ std::pair<iterator, bool> add(const Key& key, scoped_ptr<Value> data) {
+ std::pair<iterator, bool> result =
+ data_.insert(std::make_pair(key, data.get()));
+ if (result.second)
+ ignore_result(data.release());
+ return result;
}
void erase(iterator it) {
- if (it->second)
- delete it->second;
+ delete it->second;
data_.erase(it);
}
@@ -75,10 +77,8 @@ class ScopedPtrHashMap {
if (it == data_.end())
return scoped_ptr<Value>();
- Key key = it->first;
scoped_ptr<Value> ret(it->second);
- data_.erase(it);
- data_.insert(std::pair<Key, Value*>(key, static_cast<Value*>(NULL)));
+ it->second = NULL;
return ret.Pass();
}
@@ -108,12 +108,12 @@ class ScopedPtrHashMap {
return take_and_erase(it);
}
- // Returns the first element in the hash_map that matches the given key.
+ // Returns the element in the hash_map that matches the given key.
// If no such element exists it returns NULL.
Value* get(const Key& k) const {
const_iterator it = find(k);
if (it == end())
- return 0;
+ return NULL;
return it->second;
}
diff --git a/chromium/base/containers/small_map_unittest.cc b/chromium/base/containers/small_map_unittest.cc
index b911bee655d..bc76e370397 100644
--- a/chromium/base/containers/small_map_unittest.cc
+++ b/chromium/base/containers/small_map_unittest.cc
@@ -138,30 +138,21 @@ TEST(SmallMap, CopyConstructor) {
}
template<class inner>
-static void SmallMapToMap(SmallMap<inner> const& src, inner* dest) {
+static bool SmallMapIsSubset(SmallMap<inner> const& a,
+ SmallMap<inner> const& b) {
typename SmallMap<inner>::const_iterator it;
- for (it = src.begin(); it != src.end(); ++it) {
- dest->insert(std::make_pair(it->first, it->second));
+ for (it = a.begin(); it != a.end(); ++it) {
+ typename SmallMap<inner>::const_iterator it_in_b = b.find(it->first);
+ if (it_in_b == b.end() || it_in_b->second != it->second)
+ return false;
}
+ return true;
}
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;
+ return SmallMapIsSubset(a, b) && SmallMapIsSubset(b, a);
}
TEST(SmallMap, AssignmentOperator) {
diff --git a/chromium/base/containers/stack_container.h b/chromium/base/containers/stack_container.h
index f0106d73f21..87fa0369b6a 100644
--- a/chromium/base/containers/stack_container.h
+++ b/chromium/base/containers/stack_container.h
@@ -90,6 +90,13 @@ class StackAllocator : public std::allocator<T> {
: source_(NULL) {
}
+ // This constructor must exist. It creates a default allocator that doesn't
+ // actually have a stack buffer. glibc's std::string() will compare the
+ // current allocator against the default-constructed allocator, so this
+ // should be fast.
+ StackAllocator() : source_(NULL) {
+ }
+
explicit StackAllocator(Source* source) : source_(source) {
}
diff --git a/chromium/base/cpu.cc b/chromium/base/cpu.cc
index 66207a1e0b8..dcba3b6e73e 100644
--- a/chromium/base/cpu.cc
+++ b/chromium/base/cpu.cc
@@ -11,6 +11,11 @@
#include "base/basictypes.h"
#include "build/build_config.h"
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#endif
+
#if defined(ARCH_CPU_X86_FAMILY)
#if defined(_MSC_VER)
#include <intrin.h>
@@ -84,6 +89,56 @@ uint64 _xgetbv(uint32 xcr) {
#endif // !_MSC_VER
#endif // ARCH_CPU_X86_FAMILY
+#if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+
+// Returns the string found in /proc/cpuinfo under the key "model name" or
+// "Processor". "model name" is used in Linux 3.8 and later (3.7 and later for
+// arm64) and is shown once per CPU. "Processor" is used in earler versions and
+// is shown only once at the top of /proc/cpuinfo regardless of the number CPUs.
+std::string ParseCpuInfo() {
+ const char kModelNamePrefix[] = "model name\t: ";
+ const char kProcessorPrefix[] = "Processor\t: ";
+ std::string contents;
+ ReadFileToString(FilePath("/proc/cpuinfo"), &contents);
+ DCHECK(!contents.empty());
+ std::string cpu_brand;
+ if (!contents.empty()) {
+ std::istringstream iss(contents);
+ std::string line;
+ while (std::getline(iss, line)) {
+ if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) {
+ cpu_brand.assign(line.substr(strlen(kModelNamePrefix)));
+ break;
+ }
+ if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0) {
+ cpu_brand.assign(line.substr(strlen(kProcessorPrefix)));
+ break;
+ }
+ }
+ }
+ return cpu_brand;
+}
+
+class LazyCpuInfoValue {
+ public:
+ LazyCpuInfoValue() : value_(ParseCpuInfo()) {}
+ const std::string& value() { return value_; }
+
+ private:
+ const std::string value_;
+ DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue);
+};
+
+base::LazyInstance<LazyCpuInfoValue> g_lazy_cpu_brand =
+ LAZY_INSTANCE_INITIALIZER;
+
+const std::string& CpuBrandInfo() {
+ return g_lazy_cpu_brand.Get().value();
+}
+
+#endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) ||
+ // defined(OS_LINUX))
+
} // anonymous namespace
void CPU::Initialize() {
@@ -128,8 +183,14 @@ void CPU::Initialize() {
// b) XSAVE is supported by the CPU and
// c) XSAVE is enabled by the kernel.
// See http://software.intel.com/en-us/blogs/2011/04/14/is-avx-enabled
+ //
+ // In addition, we have observed some crashes with the xgetbv instruction
+ // even after following Intel's example code. (See crbug.com/375968.)
+ // Because of that, we also test the XSAVE bit because its description in
+ // the CPUID documentation suggests that it signals xgetbv support.
has_avx_ =
has_avx_hardware_ &&
+ (cpu_info[2] & 0x04000000) != 0 /* XSAVE */ &&
(cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
(_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
@@ -157,13 +218,8 @@ void CPU::Initialize() {
__cpuid(cpu_info, parameter_containing_non_stop_time_stamp_counter);
has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0;
}
-#elif defined(ARCH_CPU_ARM_FAMILY)
- // TODO(piman): Expand this. ARM has a CPUID register, but it's not available
- // in user mode. /proc/cpuinfo has some information, but it's non standard,
- // platform-specific, and not accessible from the sandbox.
- // For some purposes, this first approximation is enough.
- // crbug.com/313454
- cpu_brand_.assign("ARM");
+#elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX))
+ cpu_brand_.assign(CpuBrandInfo());
#endif
}
diff --git a/chromium/base/critical_closure.h b/chromium/base/critical_closure.h
index ca51ed5cef2..ac07911089d 100644
--- a/chromium/base/critical_closure.h
+++ b/chromium/base/critical_closure.h
@@ -7,9 +7,46 @@
#include "base/callback.h"
+#if defined(OS_IOS)
+#include "base/bind.h"
+#include "base/ios/scoped_critical_action.h"
+#endif
+
namespace base {
-// Returns a closure that will continue to run for a period of time when the
+namespace internal {
+
+#if defined(OS_IOS)
+// Returns true if multi-tasking is supported on this iOS device.
+bool IsMultiTaskingSupported();
+
+// 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
+// |ios::ScopedCriticalAction|.
+template <typename R>
+class CriticalClosure {
+ public:
+ explicit CriticalClosure(const Callback<R(void)>& closure)
+ : closure_(closure) {}
+
+ ~CriticalClosure() {}
+
+ R Run() {
+ return closure_.Run();
+ }
+
+ private:
+ ios::ScopedCriticalAction critical_action_;
+ Callback<R(void)> closure_;
+
+ DISALLOW_COPY_AND_ASSIGN(CriticalClosure);
+};
+#endif // defined(OS_IOS)
+
+} // namespace internal
+
+// Returns a closure (which may return a result, but must not require any extra
+// arguments) 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.
@@ -23,14 +60,20 @@ namespace base {
// 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) {
+template <typename R>
+Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& closure) {
+ DCHECK(internal::IsMultiTaskingSupported());
+ return base::Bind(&internal::CriticalClosure<R>::Run,
+ Owned(new internal::CriticalClosure<R>(closure)));
+}
+#else // defined(OS_IOS)
+template <typename R>
+inline Callback<R(void)> MakeCriticalClosure(const Callback<R(void)>& 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)
+#endif // defined(OS_IOS)
} // namespace base
diff --git a/chromium/base/critical_closure_internal_ios.mm b/chromium/base/critical_closure_internal_ios.mm
new file mode 100644
index 00000000000..b8fec141b21
--- /dev/null
+++ b/chromium/base/critical_closure_internal_ios.mm
@@ -0,0 +1,17 @@
+// Copyright 2014 The Chromium Authors. All 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>
+
+namespace base {
+namespace internal {
+
+bool IsMultiTaskingSupported() {
+ return [[UIDevice currentDevice] isMultitaskingSupported];
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/critical_closure_ios.mm b/chromium/base/critical_closure_ios.mm
deleted file mode 100644
index d605cad0a20..00000000000
--- a/chromium/base/critical_closure_ios.mm
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All 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) {
- }
-
- void Run() {
- closure_->Run();
- }
-
- private:
- friend class base::RefCountedThreadSafe<CriticalClosure>;
-
- virtual ~CriticalClosure() {}
-
- base::ios::ScopedCriticalAction criticial_action_;
- scoped_ptr<base::Closure> closure_;
-
- 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/chromium/base/debug/OWNERS b/chromium/base/debug/OWNERS
index 4976ab1e773..5dcc3e9d737 100644
--- a/chromium/base/debug/OWNERS
+++ b/chromium/base/debug/OWNERS
@@ -1,3 +1,5 @@
+per-file sanitizer_options.cc=glider@chromium.org
per-file trace_event*=nduca@chromium.org
per-file trace_event*=dsinclair@chromium.org
per-file trace_event_android.cc=wangxianzhu@chromium.org
+per-file tsan_suppressions.cc=*
diff --git a/chromium/base/debug/asan_invalid_access.cc b/chromium/base/debug/asan_invalid_access.cc
new file mode 100644
index 00000000000..ff7788a39cc
--- /dev/null
+++ b/chromium/base/debug/asan_invalid_access.cc
@@ -0,0 +1,94 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include "base/debug/alias.h"
+#include "base/debug/asan_invalid_access.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+namespace debug {
+
+namespace {
+
+#if defined(SYZYASAN)
+// Corrupt a memory block and make sure that the corruption gets detected either
+// when we free it or when another crash happens (if |induce_crash| is set to
+// true).
+NOINLINE void CorruptMemoryBlock(bool induce_crash) {
+ // NOTE(sebmarchand): We intentionally corrupt a memory block here in order to
+ // trigger an Address Sanitizer (ASAN) error report.
+ static const int kArraySize = 5;
+ int* array = new int[kArraySize];
+ // Encapsulate the invalid memory access into a try-catch statement to prevent
+ // this function from being instrumented. This way the underflow won't be
+ // detected but the corruption will (as the allocator will still be hooked).
+ try {
+ // Declares the dummy value as volatile to make sure it doesn't get
+ // optimized away.
+ int volatile dummy = array[-1]--;
+ base::debug::Alias(const_cast<int*>(&dummy));
+ } catch (...) {
+ }
+ if (induce_crash)
+ CHECK(false);
+ delete[] array;
+}
+#endif
+
+} // namespace
+
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+// NOTE(sebmarchand): We intentionally perform some invalid heap access here in
+// order to trigger an AddressSanitizer (ASan) error report.
+
+static const int kArraySize = 5;
+
+void AsanHeapOverflow() {
+ scoped_ptr<int[]> array(new int[kArraySize]);
+ // Declares the dummy value as volatile to make sure it doesn't get optimized
+ // away.
+ int volatile dummy = 0;
+ dummy = array[kArraySize];
+ base::debug::Alias(const_cast<int*>(&dummy));
+}
+
+void AsanHeapUnderflow() {
+ scoped_ptr<int[]> array(new int[kArraySize]);
+ // Declares the dummy value as volatile to make sure it doesn't get optimized
+ // away.
+ int volatile dummy = 0;
+ dummy = array[-1];
+ base::debug::Alias(const_cast<int*>(&dummy));
+}
+
+void AsanHeapUseAfterFree() {
+ scoped_ptr<int[]> array(new int[kArraySize]);
+ // Declares the dummy value as volatile to make sure it doesn't get optimized
+ // away.
+ int volatile dummy = 0;
+ int* dangling = array.get();
+ array.reset();
+ dummy = dangling[kArraySize / 2];
+ base::debug::Alias(const_cast<int*>(&dummy));
+}
+
+#endif // ADDRESS_SANITIZER || SYZYASAN
+
+#if defined(SYZYASAN)
+void AsanCorruptHeapBlock() {
+ CorruptMemoryBlock(false);
+}
+
+void AsanCorruptHeap() {
+ CorruptMemoryBlock(true);
+}
+#endif // SYZYASAN
+
+} // namespace debug
+} // namespace base
diff --git a/chromium/base/debug/asan_invalid_access.h b/chromium/base/debug/asan_invalid_access.h
new file mode 100644
index 00000000000..65519878f12
--- /dev/null
+++ b/chromium/base/debug/asan_invalid_access.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium 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 some functions that intentionally do an invalid memory access in
+// order to trigger an AddressSanitizer (ASan) error report.
+
+#ifndef BASE_DEBUG_ASAN_INVALID_ACCESS_H_
+#define BASE_DEBUG_ASAN_INVALID_ACCESS_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+
+namespace base {
+namespace debug {
+
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+
+// Generates an heap buffer overflow.
+BASE_EXPORT NOINLINE void AsanHeapOverflow();
+
+// Generates an heap buffer underflow.
+BASE_EXPORT NOINLINE void AsanHeapUnderflow();
+
+// Generates an use after free.
+BASE_EXPORT NOINLINE void AsanHeapUseAfterFree();
+
+#endif // ADDRESS_SANITIZER || SYZYASAN
+
+// The "corrupt-block" and "corrupt-heap" classes of bugs is specific to
+// SyzyASan.
+#if defined(SYZYASAN)
+
+// Corrupts a memory block and makes sure that the corruption gets detected when
+// we try to free this block.
+BASE_EXPORT NOINLINE void AsanCorruptHeapBlock();
+
+// Corrupts the heap and makes sure that the corruption gets detected when a
+// crash occur.
+BASE_EXPORT NOINLINE void AsanCorruptHeap();
+
+#endif // SYZYASAN
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_ASAN_INVALID_ACCESS_H_
diff --git a/chromium/base/debug/debug_on_start_win.cc b/chromium/base/debug/debug_on_start_win.cc
deleted file mode 100644
index 6ca88dde20c..00000000000
--- a/chromium/base/debug/debug_on_start_win.cc
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All 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/chromium/base/debug/debug_on_start_win.h b/chromium/base/debug/debug_on_start_win.h
deleted file mode 100644
index edcaa0aa953..00000000000
--- a/chromium/base/debug/debug_on_start_win.h
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright (c) 2011 The Chromium 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/chromium/base/debug/debugger.h b/chromium/base/debug/debugger.h
index 4f368d986d4..d62ea3f7e17 100644
--- a/chromium/base/debug/debugger.h
+++ b/chromium/base/debug/debugger.h
@@ -14,10 +14,6 @@
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);
diff --git a/chromium/base/debug/debugger_posix.cc b/chromium/base/debug/debugger_posix.cc
index 60ad5218308..48393f4f415 100644
--- a/chromium/base/debug/debugger_posix.cc
+++ b/chromium/base/debug/debugger_posix.cc
@@ -14,7 +14,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include <string>
#include <vector>
#if defined(__GLIBCXX__)
@@ -41,7 +40,6 @@
#include "base/posix/eintr_wrapper.h"
#include "base/safe_strerror_posix.h"
#include "base/strings/string_piece.h"
-#include "base/strings/stringprintf.h"
#if defined(USE_SYMBOLIZE)
#include "base/third_party/symbolize/symbolize.h"
@@ -54,22 +52,6 @@
namespace base {
namespace debug {
-bool SpawnDebuggerOnProcess(unsigned process_id) {
-#if OS_ANDROID || OS_NACL
- 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
@@ -199,8 +181,10 @@ bool BeingDebugged() {
// SIGABRT
// Mac: Always send SIGTRAP.
-#if defined(ARCH_CPU_ARM_FAMILY)
+#if defined(ARCH_CPU_ARMEL)
#define DEBUG_BREAK_ASM() asm("bkpt 0")
+#elif defined(ARCH_CPU_ARM64)
+#define DEBUG_BREAK_ASM() asm("brk 0")
#elif defined(ARCH_CPU_MIPS_FAMILY)
#define DEBUG_BREAK_ASM() asm("break 2")
#elif defined(ARCH_CPU_X86_FAMILY)
diff --git a/chromium/base/debug/debugger_win.cc b/chromium/base/debug/debugger_win.cc
index b13dbfd1483..ccc9c1604bc 100644
--- a/chromium/base/debug/debugger_win.cc
+++ b/chromium/base/debug/debugger_win.cc
@@ -4,99 +4,12 @@
#include "base/debug/debugger.h"
+#include <stdlib.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;
}
diff --git a/chromium/base/debug/dump_without_crashing.cc b/chromium/base/debug/dump_without_crashing.cc
new file mode 100644
index 00000000000..47fd873c19f
--- /dev/null
+++ b/chromium/base/debug/dump_without_crashing.cc
@@ -0,0 +1,32 @@
+// Copyright 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.
+
+#include "base/debug/dump_without_crashing.h"
+
+#include "base/logging.h"
+
+namespace {
+
+// Pointer to the function that's called by DumpWithoutCrashing() to dump the
+// process's memory.
+void (CDECL *dump_without_crashing_function_)() = NULL;
+
+} // namespace
+
+namespace base {
+
+namespace debug {
+
+void DumpWithoutCrashing() {
+ if (dump_without_crashing_function_)
+ (*dump_without_crashing_function_)();
+}
+
+void SetDumpWithoutCrashingFunction(void (CDECL *function)()) {
+ dump_without_crashing_function_ = function;
+}
+
+} // namespace debug
+
+} // namespace base
diff --git a/chromium/base/debug/dump_without_crashing.h b/chromium/base/debug/dump_without_crashing.h
new file mode 100644
index 00000000000..b8ed17414ac
--- /dev/null
+++ b/chromium/base/debug/dump_without_crashing.h
@@ -0,0 +1,30 @@
+// Copyright 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_DEBUG_DUMP_WITHOUT_CRASHING_H_
+#define BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
+
+#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace debug {
+
+// Handler to silently dump the current process without crashing.
+// Before calling this function, call SetDumpWithoutCrashingFunction to pass a
+// function pointer, typically chrome!DumpProcessWithoutCrash. See example code
+// in chrome_main.cc that does this for chrome.dll.
+BASE_EXPORT void DumpWithoutCrashing();
+
+// Sets a function that'll be invoked to dump the current process when
+// DumpWithoutCrashing() is called.
+BASE_EXPORT void SetDumpWithoutCrashingFunction(void (CDECL *function)());
+
+} // namespace debug
+
+} // namespace base
+
+#endif // BASE_DEBUG_DUMP_WITHOUT_CRASHING_H_
diff --git a/chromium/base/debug/gdi_debug_util_win.cc b/chromium/base/debug/gdi_debug_util_win.cc
new file mode 100644
index 00000000000..4bac759d8c7
--- /dev/null
+++ b/chromium/base/debug/gdi_debug_util_win.cc
@@ -0,0 +1,129 @@
+// Copyright 2014 The Chromium Authors. All 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/gdi_debug_util_win.h"
+
+#include <cmath>
+
+#include <psapi.h>
+#include <TlHelp32.h>
+
+#include "base/debug/alias.h"
+#include "base/logging.h"
+#include "base/win/scoped_handle.h"
+
+namespace {
+
+void CollectChildGDIUsageAndDie(DWORD parent_pid) {
+ HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;
+ if(snapshot == INVALID_HANDLE_VALUE)
+ CHECK(false);
+
+ int child_count = 0;
+ base::debug::Alias(&child_count);
+ int peak_gdi_count = 0;
+ base::debug::Alias(&peak_gdi_count);
+ int sum_gdi_count = 0;
+ base::debug::Alias(&sum_gdi_count);
+ int sum_user_count = 0;
+ base::debug::Alias(&sum_user_count);
+
+ PROCESSENTRY32 proc_entry = {0};
+ proc_entry.dwSize = sizeof(PROCESSENTRY32) ;
+ if(!Process32First(snapshot, &proc_entry))
+ CHECK(false);
+
+ do {
+ if (parent_pid != proc_entry.th32ParentProcessID)
+ continue;
+ // Got a child process. Compute GDI usage.
+ base::win::ScopedHandle process(
+ ::OpenProcess(PROCESS_QUERY_INFORMATION,
+ FALSE,
+ proc_entry.th32ParentProcessID));
+ if (!process)
+ continue;
+
+ int num_gdi_handles = ::GetGuiResources(process.Get(), GR_GDIOBJECTS);
+ int num_user_handles = ::GetGuiResources(process.Get(), GR_USEROBJECTS);
+
+ // Compute sum and peak counts.
+ ++child_count;
+ sum_user_count += num_user_handles;
+ sum_gdi_count += num_gdi_handles;
+ if (peak_gdi_count < num_gdi_handles)
+ peak_gdi_count = num_gdi_handles;
+
+ } while(Process32Next(snapshot, &proc_entry));
+
+ ::CloseHandle(snapshot) ;
+ CHECK(false);
+}
+
+} // namespace
+
+namespace base {
+namespace debug {
+
+void GDIBitmapAllocFailure(BITMAPINFOHEADER* header, HANDLE shared_section) {
+ // Make sure parameters are saved in the minidump.
+ DWORD last_error = ::GetLastError();
+
+ LONG width = header->biWidth;
+ LONG heigth = header->biHeight;
+
+ base::debug::Alias(&last_error);
+ base::debug::Alias(&width);
+ base::debug::Alias(&heigth);
+ base::debug::Alias(&shared_section);
+
+ int num_user_handles = GetGuiResources(GetCurrentProcess(),
+ GR_USEROBJECTS);
+
+ int num_gdi_handles = GetGuiResources(GetCurrentProcess(),
+ GR_GDIOBJECTS);
+ if (num_gdi_handles == 0) {
+ DWORD get_gui_resources_error = GetLastError();
+ base::debug::Alias(&get_gui_resources_error);
+ CHECK(false);
+ }
+
+ base::debug::Alias(&num_gdi_handles);
+ base::debug::Alias(&num_user_handles);
+
+ const DWORD kLotsOfHandles = 9990;
+ if (num_gdi_handles > kLotsOfHandles)
+ CHECK(false);
+
+ PROCESS_MEMORY_COUNTERS_EX pmc;
+ pmc.cb = sizeof(pmc);
+ if (!GetProcessMemoryInfo(GetCurrentProcess(),
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc),
+ sizeof(pmc))) {
+ CHECK(false);
+ }
+ const size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
+ if (pmc.PagefileUsage > kLotsOfMemory)
+ CHECK(false);
+ if (pmc.PrivateUsage > kLotsOfMemory)
+ CHECK(false);
+
+ void* small_data = NULL;
+ base::debug::Alias(&small_data);
+
+ if (std::abs(heigth) * width > 100) {
+ // Huh, that's weird. We don't have crazy handle count, we don't have
+ // ridiculous memory usage. Try to allocate a small bitmap and see if that
+ // fails too.
+ header->biWidth = 5;
+ header->biHeight = -5;
+ HBITMAP small_bitmap = CreateDIBSection(
+ NULL, reinterpret_cast<BITMAPINFO*>(&header),
+ 0, &small_data, shared_section, 0);
+ }
+ // Maybe the child processes are the ones leaking GDI or USER resouces.
+ CollectChildGDIUsageAndDie(::GetCurrentProcessId());
+}
+
+} // namespace debug
+} // namespace base
diff --git a/chromium/base/debug/gdi_debug_util_win.h b/chromium/base/debug/gdi_debug_util_win.h
new file mode 100644
index 00000000000..5887ecb8464
--- /dev/null
+++ b/chromium/base/debug/gdi_debug_util_win.h
@@ -0,0 +1,23 @@
+// Copyright 2014 The Chromium Authors. All 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_GDI_DEBUG_UTIL_WIN_H_
+#define BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
+
+#include <windows.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace debug {
+
+// Crashes the process leaving valuable information on the dump via
+// debug::alias so we can find what is causing the allocation failures.
+void BASE_EXPORT GDIBitmapAllocFailure(BITMAPINFOHEADER* header,
+ HANDLE shared_section);
+
+} // namespace debug
+} // namespace base
+
+#endif // BASE_DEBUG_GDI_DEBUG_UTIL_WIN_H_
diff --git a/chromium/base/debug/leak_tracker.h b/chromium/base/debug/leak_tracker.h
index e5f0cb1778b..8c5aaf31c2f 100644
--- a/chromium/base/debug/leak_tracker.h
+++ b/chromium/base/debug/leak_tracker.h
@@ -5,8 +5,10 @@
#ifndef BASE_DEBUG_LEAK_TRACKER_H_
#define BASE_DEBUG_LEAK_TRACKER_H_
-// Only enable leak tracking in debug builds.
-#ifndef NDEBUG
+#include "build/build_config.h"
+
+// Only enable leak tracking in non-uClibc debug builds.
+#if !defined(NDEBUG) && !defined(__UCLIBC__)
#define ENABLE_LEAK_TRACKER
#endif
diff --git a/chromium/base/debug/proc_maps_linux.cc b/chromium/base/debug/proc_maps_linux.cc
index b7a5862f747..1e0209e9bf7 100644
--- a/chromium/base/debug/proc_maps_linux.cc
+++ b/chromium/base/debug/proc_maps_linux.cc
@@ -6,16 +6,18 @@
#include <fcntl.h>
-#if defined(OS_LINUX)
+#if defined(OS_LINUX) || defined(OS_ANDROID)
#include <inttypes.h>
#endif
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/strings/string_split.h"
-#if defined(OS_ANDROID)
-// Bionic's inttypes.h defines PRI/SCNxPTR as an unsigned long int, which
-// is incompatible with Bionic's stdint.h defining uintptr_t as a unsigned int:
+#if defined(OS_ANDROID) && !defined(__LP64__)
+// In 32-bit mode, Bionic's inttypes.h defines PRI/SCNxPTR as an
+// unsigned long int, which is incompatible with Bionic's stdint.h
+// defining uintptr_t as an unsigned int:
// https://code.google.com/p/android/issues/detail?id=57218
#undef SCNxPTR
#define SCNxPTR "x"
@@ -45,12 +47,11 @@ bool ReadProcMaps(std::string* proc_maps) {
// file for details.
const long kReadSize = sysconf(_SC_PAGESIZE);
- int fd = HANDLE_EINTR(open("/proc/self/maps", O_RDONLY));
- if (fd == -1) {
+ base::ScopedFD fd(HANDLE_EINTR(open("/proc/self/maps", O_RDONLY)));
+ if (!fd.is_valid()) {
DPLOG(ERROR) << "Couldn't open /proc/self/maps";
return false;
}
- file_util::ScopedFD fd_closer(&fd);
proc_maps->clear();
while (true) {
@@ -60,7 +61,7 @@ bool ReadProcMaps(std::string* proc_maps) {
proc_maps->resize(pos + kReadSize);
void* buffer = &(*proc_maps)[pos];
- ssize_t bytes_read = HANDLE_EINTR(read(fd, buffer, kReadSize));
+ ssize_t bytes_read = HANDLE_EINTR(read(fd.get(), buffer, kReadSize));
if (bytes_read < 0) {
DPLOG(ERROR) << "Couldn't read /proc/self/maps";
proc_maps->clear();
@@ -90,6 +91,7 @@ bool ReadProcMaps(std::string* proc_maps) {
bool ParseProcMaps(const std::string& input,
std::vector<MappedMemoryRegion>* regions_out) {
+ CHECK(regions_out);
std::vector<MappedMemoryRegion> regions;
// This isn't async safe nor terribly efficient, but it doesn't need to be at
@@ -100,8 +102,10 @@ bool ParseProcMaps(const std::string& input,
for (size_t i = 0; i < lines.size(); ++i) {
// Due to splitting on '\n' the last line should be empty.
if (i == lines.size() - 1) {
- if (!lines[i].empty())
+ if (!lines[i].empty()) {
+ DLOG(WARNING) << "Last line not empty";
return false;
+ }
break;
}
@@ -124,6 +128,7 @@ bool ParseProcMaps(const std::string& input,
if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " %4c %llx %hhx:%hhx %ld %n",
&region.start, &region.end, permissions, &region.offset,
&dev_major, &dev_minor, &inode, &path_index) < 7) {
+ DPLOG(WARNING) << "sscanf failed for line: " << line;
return false;
}
diff --git a/chromium/base/debug/proc_maps_linux_unittest.cc b/chromium/base/debug/proc_maps_linux_unittest.cc
index 7c2929f212a..fc8ced6aa05 100644
--- a/chromium/base/debug/proc_maps_linux_unittest.cc
+++ b/chromium/base/debug/proc_maps_linux_unittest.cc
@@ -277,5 +277,37 @@ TEST(ProcMapsTest, InvalidInput) {
}
}
+TEST(ProcMapsTest, ParseProcMapsEmptyString) {
+ std::vector<MappedMemoryRegion> regions;
+ EXPECT_TRUE(ParseProcMaps("", &regions));
+ EXPECT_EQ(0ULL, regions.size());
+}
+
+// Testing a couple of remotely possible weird things in the input:
+// - Line ending with \r\n or \n\r.
+// - File name contains quotes.
+// - File name has whitespaces.
+TEST(ProcMapsTest, ParseProcMapsWeirdCorrectInput) {
+ std::vector<MappedMemoryRegion> regions;
+ const std::string kContents =
+ "00400000-0040b000 r-xp 00000000 fc:00 2106562 "
+ " /bin/cat\r\n"
+ "7f53b7dad000-7f53b7f62000 r-xp 00000000 fc:00 263011 "
+ " /lib/x86_64-linux-gnu/libc-2.15.so\n\r"
+ "7f53b816d000-7f53b818f000 r-xp 00000000 fc:00 264284 "
+ " /lib/x86_64-linux-gnu/ld-2.15.so\n"
+ "7fff9c7ff000-7fff9c800000 r-xp 00000000 00:00 0 "
+ " \"vd so\"\n"
+ "ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 "
+ " [vsys call]\n";
+ EXPECT_TRUE(ParseProcMaps(kContents, &regions));
+ EXPECT_EQ(5ULL, regions.size());
+ EXPECT_EQ("/bin/cat", regions[0].path);
+ EXPECT_EQ("/lib/x86_64-linux-gnu/libc-2.15.so", regions[1].path);
+ EXPECT_EQ("/lib/x86_64-linux-gnu/ld-2.15.so", regions[2].path);
+ EXPECT_EQ("\"vd so\"", regions[3].path);
+ EXPECT_EQ("[vsys call]", regions[4].path);
+}
+
} // namespace debug
} // namespace base
diff --git a/chromium/base/debug/sanitizer_options.cc b/chromium/base/debug/sanitizer_options.cc
new file mode 100644
index 00000000000..7b26876ea85
--- /dev/null
+++ b/chromium/base/debug/sanitizer_options.cc
@@ -0,0 +1,117 @@
+// Copyright 2014 The Chromium 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 default options for various compiler-based dynamic
+// tools.
+
+#include "build/build_config.h"
+
+// Functions returning default options are declared weak in the tools' runtime
+// libraries. To make the linker pick the strong replacements for those
+// functions from this module, we explicitly force its inclusion by passing
+// -Wl,-u_sanitizer_options_link_helper
+extern "C"
+void _sanitizer_options_link_helper() { }
+
+#if defined(ADDRESS_SANITIZER)
+// Default options for AddressSanitizer in various configurations:
+// strict_memcmp=1 - disable the strict memcmp() checking
+// (http://crbug.com/178677 and http://crbug.com/178404).
+// malloc_context_size=5 - limit the size of stack traces collected by ASan
+// for each malloc/free by 5 frames. These stack traces tend to accumulate
+// very fast in applications using JIT (v8 in Chrome's case), see
+// https://code.google.com/p/address-sanitizer/issues/detail?id=177
+// symbolize=false - disable the in-process symbolization, which isn't 100%
+// compatible with the existing sandboxes and doesn't make much sense for
+// stripped official binaries.
+// legacy_pthread_cond=1 - run in the libpthread 2.2.5 compatibility mode to
+// work around libGL.so using the obsolete API, see
+// http://crbug.com/341805. This may break if pthread_cond_t objects are
+// accessed by both instrumented and non-instrumented binaries (e.g. if
+// they reside in shared memory). This option is going to be deprecated in
+// upstream AddressSanitizer and must not be used anywhere except the
+// official builds.
+// replace_intrin=0 - do not intercept memcpy(), memmove() and memset() to
+// work around http://crbug.com/162461 (ASan report in OpenCL on Mac).
+// check_printf=1 - check the memory accesses to printf (and other formatted
+// output routines) arguments.
+// use_sigaltstack=1 - handle signals on an alternate signal stack. Useful
+// for stack overflow detection.
+// strip_path_prefix=Release/../../ - prefixes up to and including this
+// substring will be stripped from source file paths in symbolized reports
+// (if symbolize=true, which is set when running with LeakSanitizer).
+#if defined(OS_LINUX)
+#if defined(GOOGLE_CHROME_BUILD)
+// Default AddressSanitizer options for the official build. These do not affect
+// tests on buildbots (which don't set GOOGLE_CHROME_BUILD) or non-official
+// Chromium builds.
+const char kAsanDefaultOptions[] =
+ "legacy_pthread_cond=1 malloc_context_size=5 strict_memcmp=0 "
+ "symbolize=false check_printf=1 use_sigaltstack=1 detect_leaks=0 "
+ "strip_path_prefix=Release/../../ ";
+#else
+// Default AddressSanitizer options for buildbots and non-official builds.
+const char *kAsanDefaultOptions =
+ "strict_memcmp=0 symbolize=false check_printf=1 use_sigaltstack=1 "
+ "detect_leaks=0 strip_path_prefix=Release/../../ ";
+#endif // GOOGLE_CHROME_BUILD
+
+#elif defined(OS_MACOSX)
+const char *kAsanDefaultOptions =
+ "strict_memcmp=0 replace_intrin=0 check_printf=1 use_sigaltstack=1 "
+ "strip_path_prefix=Release/../../ ";
+#endif // OS_LINUX
+
+#if defined(OS_LINUX) || defined(OS_MACOSX)
+extern "C"
+__attribute__((no_sanitize_address))
+__attribute__((visibility("default")))
+// The function isn't referenced from the executable itself. Make sure it isn't
+// stripped by the linker.
+__attribute__((used))
+const char *__asan_default_options() {
+ return kAsanDefaultOptions;
+}
+#endif // OS_LINUX || OS_MACOSX
+#endif // ADDRESS_SANITIZER
+
+#if defined(THREAD_SANITIZER) && defined(OS_LINUX)
+// Default options for ThreadSanitizer in various configurations:
+// detect_deadlocks=1 - enable deadlock (lock inversion) detection.
+// second_deadlock_stack=1 - more verbose deadlock reports.
+// report_signal_unsafe=0 - do not report async-signal-unsafe functions
+// called from signal handlers.
+// report_thread_leaks=0 - do not report unjoined threads at the end of
+// the program execution.
+// print_suppressions=1 - print the list of matched suppressions.
+// strip_path_prefix=Release/../../ - prefixes up to and including this
+// substring will be stripped from source file paths in symbolized reports.
+const char kTsanDefaultOptions[] =
+ "detect_deadlocks=1 second_deadlock_stack=1 report_signal_unsafe=0 "
+ "report_thread_leaks=0 print_suppressions=1 "
+ "strip_path_prefix=Release/../../ ";
+
+extern "C"
+__attribute__((no_sanitize_thread))
+__attribute__((visibility("default")))
+// The function isn't referenced from the executable itself. Make sure it isn't
+// stripped by the linker.
+__attribute__((used))
+const char *__tsan_default_options() {
+ return kTsanDefaultOptions;
+}
+
+extern "C" char kTSanDefaultSuppressions[];
+
+extern "C"
+__attribute__((no_sanitize_thread))
+__attribute__((visibility("default")))
+// The function isn't referenced from the executable itself. Make sure it isn't
+// stripped by the linker.
+__attribute__((used))
+const char *__tsan_default_suppressions() {
+ return kTSanDefaultSuppressions;
+}
+
+#endif // THREAD_SANITIZER && OS_LINUX
diff --git a/chromium/base/debug/stack_trace.cc b/chromium/base/debug/stack_trace.cc
index 6fab1835034..ce9e9ad5500 100644
--- a/chromium/base/debug/stack_trace.cc
+++ b/chromium/base/debug/stack_trace.cc
@@ -33,7 +33,9 @@ const void *const *StackTrace::Addresses(size_t* count) const {
std::string StackTrace::ToString() const {
std::stringstream stream;
+#if !defined(__UCLIBC__)
OutputToStream(&stream);
+#endif
return stream.str();
}
diff --git a/chromium/base/debug/stack_trace.h b/chromium/base/debug/stack_trace.h
index b0883c1fc92..7c2ac3cb87c 100644
--- a/chromium/base/debug/stack_trace.h
+++ b/chromium/base/debug/stack_trace.h
@@ -27,6 +27,15 @@ namespace debug {
// unit_tests only! This is not thread-safe: only call from main thread.
BASE_EXPORT bool EnableInProcessStackDumping();
+// A different version of EnableInProcessStackDumping that also works for
+// sandboxed processes. For more details take a look at the description
+// of EnableInProcessStackDumping.
+// Calling this function on Linux opens /proc/self/maps and caches its
+// contents. In DEBUG builds, this function also opens the object files that
+// are loaded in memory and caches their file descriptors (this cannot be
+// done in official builds because it has security implications).
+BASE_EXPORT bool EnableInProcessStackDumpingForSandbox();
+
// 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.
@@ -44,7 +53,7 @@ class BASE_EXPORT StackTrace {
// 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);
+ StackTrace(const _EXCEPTION_POINTERS* exception_pointers);
#endif
// Copying and assignment are allowed with the default functions.
@@ -55,11 +64,13 @@ class BASE_EXPORT StackTrace {
// number of elements in the returned array.
const void* const* Addresses(size_t* count) const;
+#if !defined(__UCLIBC__)
// Prints the stack trace to stderr.
void Print() const;
// Resolves backtrace to symbols and write to stream.
void OutputToStream(std::ostream* os) const;
+#endif
// Resolves backtrace to symbols and returns as string.
std::string ToString() const;
diff --git a/chromium/base/debug/stack_trace_android.cc b/chromium/base/debug/stack_trace_android.cc
index 257e82309e7..c07f34a9957 100644
--- a/chromium/base/debug/stack_trace_android.cc
+++ b/chromium/base/debug/stack_trace_android.cc
@@ -11,6 +11,12 @@
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
+#ifdef __LP64__
+#define FMT_ADDR "0x%016lx"
+#else
+#define FMT_ADDR "0x%08x"
+#endif
+
namespace {
struct StackCrawlState {
@@ -104,12 +110,12 @@ void StackTrace::OutputToStream(std::ostream* os) const {
++iter;
}
- *os << base::StringPrintf("#%02d 0x%08x ", i, address);
+ *os << base::StringPrintf("#%02zd " FMT_ADDR " ", i, address);
if (iter != regions.end()) {
uintptr_t rel_pc = address - iter->start + iter->offset;
const char* path = iter->path.c_str();
- *os << base::StringPrintf("%s+0x%08x", path, rel_pc);
+ *os << base::StringPrintf("%s+" FMT_ADDR, path, rel_pc);
} else {
*os << "<unknown>";
}
diff --git a/chromium/base/debug/stack_trace_ios.mm b/chromium/base/debug/stack_trace_ios.mm
deleted file mode 100644
index 998dd0dfc69..00000000000
--- a/chromium/base/debug/stack_trace_ios.mm
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import <Foundation/Foundation.h>
-#include <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/chromium/base/debug/stack_trace_posix.cc b/chromium/base/debug/stack_trace_posix.cc
index ed1a91889b1..261ce9587d3 100644
--- a/chromium/base/debug/stack_trace_posix.cc
+++ b/chromium/base/debug/stack_trace_posix.cc
@@ -5,7 +5,6 @@
#include "base/debug/stack_trace.h"
#include <errno.h>
-#include <execinfo.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
@@ -15,11 +14,17 @@
#include <sys/types.h>
#include <unistd.h>
+#include <map>
#include <ostream>
+#include <string>
+#include <vector>
#if defined(__GLIBCXX__)
#include <cxxabi.h>
#endif
+#if !defined(__UCLIBC__)
+#include <execinfo.h>
+#endif
#if defined(OS_MACOSX)
#include <AvailabilityMacros.h>
@@ -27,10 +32,14 @@
#include "base/basictypes.h"
#include "base/debug/debugger.h"
+#include "base/debug/proc_maps_linux.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/numerics/safe_conversions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
#if defined(USE_SYMBOLIZE)
#include "base/third_party/symbolize/symbolize.h"
@@ -64,7 +73,7 @@ void DemangleSymbols(std::string* text) {
// Note: code in this function is NOT async-signal safe (std::string uses
// malloc internally).
-#if defined(__GLIBCXX__)
+#if defined(__GLIBCXX__) && !defined(__UCLIBC__)
std::string::size_type search_from = 0;
while (search_from < text->size()) {
@@ -86,7 +95,7 @@ void DemangleSymbols(std::string* text) {
// Try to demangle the mangled symbol candidate.
int status = 0;
- scoped_ptr_malloc<char> demangled_symbol(
+ scoped_ptr<char, base::FreeDeleter> demangled_symbol(
abi::__cxa_demangle(mangled_symbol.c_str(), NULL, 0, &status));
if (status == 0) { // Demangling is successful.
// Remove the mangled symbol.
@@ -101,7 +110,7 @@ void DemangleSymbols(std::string* text) {
}
}
-#endif // defined(__GLIBCXX__)
+#endif // defined(__GLIBCXX__) && !defined(__UCLIBC__)
}
#endif // !defined(USE_SYMBOLIZE)
@@ -114,22 +123,37 @@ class BacktraceOutputHandler {
};
void OutputPointer(void* pointer, BacktraceOutputHandler* handler) {
- char buf[1024] = { '\0' };
- handler->HandleOutput(" [0x");
+ // This should be more than enough to store a 64-bit number in hex:
+ // 16 hex digits + 1 for null-terminator.
+ char buf[17] = { '\0' };
+ handler->HandleOutput("0x");
internal::itoa_r(reinterpret_cast<intptr_t>(pointer),
buf, sizeof(buf), 16, 12);
handler->HandleOutput(buf);
- handler->HandleOutput("]");
}
+#if defined(USE_SYMBOLIZE)
+void OutputFrameId(intptr_t frame_id, BacktraceOutputHandler* handler) {
+ // Max unsigned 64-bit number in decimal has 20 digits (18446744073709551615).
+ // Hence, 30 digits should be more than enough to represent it in decimal
+ // (including the null-terminator).
+ char buf[30] = { '\0' };
+ handler->HandleOutput("#");
+ internal::itoa_r(frame_id, buf, sizeof(buf), 10, 1);
+ handler->HandleOutput(buf);
+}
+#endif // defined(USE_SYMBOLIZE)
+
void ProcessBacktrace(void *const *trace,
- int size,
+ size_t 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) {
+ for (size_t i = 0; i < size; ++i) {
+ OutputFrameId(i, handler);
+ handler->HandleOutput(" ");
OutputPointer(trace[i], handler);
handler->HandleOutput(" ");
@@ -145,15 +169,16 @@ void ProcessBacktrace(void *const *trace,
handler->HandleOutput("\n");
}
-#else
+#elif !defined(__UCLIBC__)
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) {
- scoped_ptr_malloc<char*> trace_symbols(backtrace_symbols(trace, size));
+ scoped_ptr<char*, FreeDeleter>
+ trace_symbols(backtrace_symbols(trace, size));
if (trace_symbols.get()) {
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
std::string trace_symbol = trace_symbols.get()[i];
DemangleSymbols(&trace_symbol);
handler->HandleOutput(trace_symbol.c_str());
@@ -165,9 +190,10 @@ void ProcessBacktrace(void *const *trace,
}
if (!printed) {
- for (int i = 0; i < size; ++i) {
+ for (size_t i = 0; i < size; ++i) {
+ handler->HandleOutput(" [");
OutputPointer(trace[i], handler);
- handler->HandleOutput("\n");
+ handler->HandleOutput("]\n");
}
}
#endif // defined(USE_SYMBOLIZE)
@@ -179,7 +205,6 @@ void PrintToStderr(const char* output) {
ignore_result(HANDLE_EINTR(write(STDERR_FILENO, output, strlen(output))));
}
-#if !defined(OS_IOS)
void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
// NOTE: This code MUST be async-signal safe.
// NO malloc or stdio is allowed here.
@@ -256,7 +281,9 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
}
PrintToStderr("\n");
+#if !defined(__UCLIBC__)
debug::StackTrace().Print();
+#endif
#if defined(OS_LINUX)
#if ARCH_CPU_X86_FAMILY
@@ -330,7 +357,7 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
PrintToStderr("\n");
#endif
#elif defined(OS_MACOSX)
- // TODO(shess): Port to 64-bit.
+ // TODO(shess): Port to 64-bit, and ARM architecture (32 and 64-bit).
#if ARCH_CPU_X86_FAMILY && ARCH_CPU_32_BITS
ucontext_t* context = reinterpret_cast<ucontext_t*>(void_context);
size_t len;
@@ -372,7 +399,6 @@ void StackDumpSignalHandler(int signal, siginfo_t* info, void* void_context) {
#endif // defined(OS_MACOSX)
_exit(1);
}
-#endif // !defined(OS_IOS)
class PrintBacktraceOutputHandler : public BacktraceOutputHandler {
public:
@@ -403,7 +429,6 @@ class StreamBacktraceOutputHandler : public BacktraceOutputHandler {
DISALLOW_COPY_AND_ASSIGN(StreamBacktraceOutputHandler);
};
-#if !defined(OS_IOS)
void WarmUpBacktrace() {
// Warm up stack trace infrastructure. It turns out that on the first
// call glibc initializes some internal data structures using pthread_once,
@@ -436,11 +461,251 @@ void WarmUpBacktrace() {
// #22 <signal handler called>
StackTrace stack_trace;
}
-#endif // !defined(OS_IOS)
} // namespace
-#if !defined(OS_IOS)
+#if defined(USE_SYMBOLIZE)
+
+// class SandboxSymbolizeHelper.
+//
+// The purpose of this class is to prepare and install a "file open" callback
+// needed by the stack trace symbolization code
+// (base/third_party/symbolize/symbolize.h) so that it can function properly
+// in a sandboxed process. The caveat is that this class must be instantiated
+// before the sandboxing is enabled so that it can get the chance to open all
+// the object files that are loaded in the virtual address space of the current
+// process.
+class SandboxSymbolizeHelper {
+ public:
+ // Returns the singleton instance.
+ static SandboxSymbolizeHelper* GetInstance() {
+ return Singleton<SandboxSymbolizeHelper>::get();
+ }
+
+ private:
+ friend struct DefaultSingletonTraits<SandboxSymbolizeHelper>;
+
+ SandboxSymbolizeHelper()
+ : is_initialized_(false) {
+ Init();
+ }
+
+ ~SandboxSymbolizeHelper() {
+ UnregisterCallback();
+ CloseObjectFiles();
+ }
+
+ // Returns a O_RDONLY file descriptor for |file_path| if it was opened
+ // sucessfully during the initialization. The file is repositioned at
+ // offset 0.
+ // IMPORTANT: This function must be async-signal-safe because it can be
+ // called from a signal handler (symbolizing stack frames for a crash).
+ int GetFileDescriptor(const char* file_path) {
+ int fd = -1;
+
+#if !defined(NDEBUG)
+ if (file_path) {
+ // The assumption here is that iterating over std::map<std::string, int>
+ // using a const_iterator does not allocate dynamic memory, hense it is
+ // async-signal-safe.
+ std::map<std::string, int>::const_iterator it;
+ for (it = modules_.begin(); it != modules_.end(); ++it) {
+ if (strcmp((it->first).c_str(), file_path) == 0) {
+ // POSIX.1-2004 requires an implementation to guarantee that dup()
+ // is async-signal-safe.
+ fd = dup(it->second);
+ break;
+ }
+ }
+ // POSIX.1-2004 requires an implementation to guarantee that lseek()
+ // is async-signal-safe.
+ if (fd >= 0 && lseek(fd, 0, SEEK_SET) < 0) {
+ // Failed to seek.
+ fd = -1;
+ }
+ }
+#endif // !defined(NDEBUG)
+
+ return fd;
+ }
+
+ // Searches for the object file (from /proc/self/maps) that contains
+ // the specified pc. If found, sets |start_address| to the start address
+ // of where this object file is mapped in memory, sets the module base
+ // address into |base_address|, copies the object file name into
+ // |out_file_name|, and attempts to open the object file. If the object
+ // file is opened successfully, returns the file descriptor. Otherwise,
+ // returns -1. |out_file_name_size| is the size of the file name buffer
+ // (including the null terminator).
+ // IMPORTANT: This function must be async-signal-safe because it can be
+ // called from a signal handler (symbolizing stack frames for a crash).
+ static int OpenObjectFileContainingPc(uint64_t pc, uint64_t& start_address,
+ uint64_t& base_address, char* file_path,
+ int file_path_size) {
+ // This method can only be called after the singleton is instantiated.
+ // This is ensured by the following facts:
+ // * This is the only static method in this class, it is private, and
+ // the class has no friends (except for the DefaultSingletonTraits).
+ // The compiler guarantees that it can only be called after the
+ // singleton is instantiated.
+ // * This method is used as a callback for the stack tracing code and
+ // the callback registration is done in the constructor, so logically
+ // it cannot be called before the singleton is created.
+ SandboxSymbolizeHelper* instance = GetInstance();
+
+ // The assumption here is that iterating over
+ // std::vector<MappedMemoryRegion> using a const_iterator does not allocate
+ // dynamic memory, hence it is async-signal-safe.
+ std::vector<MappedMemoryRegion>::const_iterator it;
+ bool is_first = true;
+ for (it = instance->regions_.begin(); it != instance->regions_.end();
+ ++it, is_first = false) {
+ const MappedMemoryRegion& region = *it;
+ if (region.start <= pc && pc < region.end) {
+ start_address = region.start;
+ // Don't subtract 'start_address' from the first entry:
+ // * If a binary is compiled w/o -pie, then the first entry in
+ // process maps is likely the binary itself (all dynamic libs
+ // are mapped higher in address space). For such a binary,
+ // instruction offset in binary coincides with the actual
+ // instruction address in virtual memory (as code section
+ // is mapped to a fixed memory range).
+ // * If a binary is compiled with -pie, all the modules are
+ // mapped high at address space (in particular, higher than
+ // shadow memory of the tool), so the module can't be the
+ // first entry.
+ base_address = (is_first ? 0U : start_address) - region.offset;
+ if (file_path && file_path_size > 0) {
+ strncpy(file_path, region.path.c_str(), file_path_size);
+ // Ensure null termination.
+ file_path[file_path_size - 1] = '\0';
+ }
+ return instance->GetFileDescriptor(region.path.c_str());
+ }
+ }
+ return -1;
+ }
+
+ // Parses /proc/self/maps in order to compile a list of all object file names
+ // for the modules that are loaded in the current process.
+ // Returns true on success.
+ bool CacheMemoryRegions() {
+ // Reads /proc/self/maps.
+ std::string contents;
+ if (!ReadProcMaps(&contents)) {
+ LOG(ERROR) << "Failed to read /proc/self/maps";
+ return false;
+ }
+
+ // Parses /proc/self/maps.
+ if (!ParseProcMaps(contents, &regions_)) {
+ LOG(ERROR) << "Failed to parse the contents of /proc/self/maps";
+ return false;
+ }
+
+ is_initialized_ = true;
+ return true;
+ }
+
+ // Opens all object files and caches their file descriptors.
+ void OpenSymbolFiles() {
+ // Pre-opening and caching the file descriptors of all loaded modules is
+ // not considered safe for retail builds. Hence it is only done in debug
+ // builds. For more details, take a look at: http://crbug.com/341966
+ // Enabling this to release mode would require approval from the security
+ // team.
+#if !defined(NDEBUG)
+ // Open the object files for all read-only executable regions and cache
+ // their file descriptors.
+ std::vector<MappedMemoryRegion>::const_iterator it;
+ for (it = regions_.begin(); it != regions_.end(); ++it) {
+ const MappedMemoryRegion& region = *it;
+ // Only interesed in read-only executable regions.
+ if ((region.permissions & MappedMemoryRegion::READ) ==
+ MappedMemoryRegion::READ &&
+ (region.permissions & MappedMemoryRegion::WRITE) == 0 &&
+ (region.permissions & MappedMemoryRegion::EXECUTE) ==
+ MappedMemoryRegion::EXECUTE) {
+ if (region.path.empty()) {
+ // Skip regions with empty file names.
+ continue;
+ }
+ if (region.path[0] == '[') {
+ // Skip pseudo-paths, like [stack], [vdso], [heap], etc ...
+ continue;
+ }
+ // Avoid duplicates.
+ if (modules_.find(region.path) == modules_.end()) {
+ int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC);
+ if (fd >= 0) {
+ modules_.insert(std::make_pair(region.path, fd));
+ } else {
+ LOG(WARNING) << "Failed to open file: " << region.path
+ << "\n Error: " << strerror(errno);
+ }
+ }
+ }
+ }
+#endif // !defined(NDEBUG)
+ }
+
+ // Initializes and installs the symbolization callback.
+ void Init() {
+ if (CacheMemoryRegions()) {
+ OpenSymbolFiles();
+ google::InstallSymbolizeOpenObjectFileCallback(
+ &OpenObjectFileContainingPc);
+ }
+ }
+
+ // Unregister symbolization callback.
+ void UnregisterCallback() {
+ if (is_initialized_) {
+ google::InstallSymbolizeOpenObjectFileCallback(NULL);
+ is_initialized_ = false;
+ }
+ }
+
+ // Closes all file descriptors owned by this instance.
+ void CloseObjectFiles() {
+#if !defined(NDEBUG)
+ std::map<std::string, int>::iterator it;
+ for (it = modules_.begin(); it != modules_.end(); ++it) {
+ int ret = IGNORE_EINTR(close(it->second));
+ DCHECK(!ret);
+ it->second = -1;
+ }
+ modules_.clear();
+#endif // !defined(NDEBUG)
+ }
+
+ // Set to true upon successful initialization.
+ bool is_initialized_;
+
+#if !defined(NDEBUG)
+ // Mapping from file name to file descriptor. Includes file descriptors
+ // for all successfully opened object files and the file descriptor for
+ // /proc/self/maps. This code is not safe for release builds so
+ // this is only done for DEBUG builds.
+ std::map<std::string, int> modules_;
+#endif // !defined(NDEBUG)
+
+ // Cache for the process memory regions. Produced by parsing the contents
+ // of /proc/self/maps cache.
+ std::vector<MappedMemoryRegion> regions_;
+
+ DISALLOW_COPY_AND_ASSIGN(SandboxSymbolizeHelper);
+};
+#endif // USE_SYMBOLIZE
+
+bool EnableInProcessStackDumpingForSandbox() {
+#if defined(USE_SYMBOLIZE)
+ SandboxSymbolizeHelper::GetInstance();
+#endif // USE_SYMBOLIZE
+
+ return EnableInProcessStackDumping();
+}
+
bool EnableInProcessStackDumping() {
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
@@ -465,21 +730,28 @@ bool EnableInProcessStackDumping() {
success &= (sigaction(SIGFPE, &action, NULL) == 0);
success &= (sigaction(SIGBUS, &action, NULL) == 0);
success &= (sigaction(SIGSEGV, &action, NULL) == 0);
+// On Linux, SIGSYS is reserved by the kernel for seccomp-bpf sandboxing.
+#if !defined(OS_LINUX)
success &= (sigaction(SIGSYS, &action, NULL) == 0);
+#endif // !defined(OS_LINUX)
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.
+#if !defined(__UCLIBC__)
// 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);
+ count_ = base::saturated_cast<size_t>(backtrace(trace_, arraysize(trace_)));
+#else
+ count_ = 0;
+#endif
}
+#if !defined(__UCLIBC__)
void StackTrace::Print() 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.
@@ -492,6 +764,7 @@ void StackTrace::OutputToStream(std::ostream* os) const {
StreamBacktraceOutputHandler handler(os);
ProcessBacktrace(trace_, count_, &handler);
}
+#endif
namespace internal {
diff --git a/chromium/base/debug/stack_trace_unittest.cc b/chromium/base/debug/stack_trace_unittest.cc
index 701ebc47a1f..eb0bd9ad7f3 100644
--- a/chromium/base/debug/stack_trace_unittest.cc
+++ b/chromium/base/debug/stack_trace_unittest.cc
@@ -35,6 +35,7 @@ typedef testing::Test StackTraceTest;
#else
#define MAYBE_OutputToStream OutputToStream
#endif
+#if !defined(__UCLIBC__)
TEST_F(StackTraceTest, MAYBE_OutputToStream) {
StackTrace trace;
@@ -130,6 +131,7 @@ TEST_F(StackTraceTest, DebugOutputToStream) {
TEST_F(StackTraceTest, DebugPrintBacktrace) {
StackTrace().Print();
}
+#endif // !defined(__UCLIBC__)
#if defined(OS_POSIX) && !defined(OS_ANDROID)
#if !defined(OS_IOS)
@@ -144,7 +146,7 @@ MULTIPROCESS_TEST_MAIN(MismatchedMallocChildProcess) {
// 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);
+ ProcessHandle child = SpawnChild("MismatchedMallocChildProcess");
ASSERT_NE(kNullProcessHandle, child);
ASSERT_TRUE(WaitForSingleProcess(child, TestTimeouts::action_timeout()));
}
diff --git a/chromium/base/debug/stack_trace_win.cc b/chromium/base/debug/stack_trace_win.cc
index eb35b6ab91b..a3327afcea8 100644
--- a/chromium/base/debug/stack_trace_win.cc
+++ b/chromium/base/debug/stack_trace_win.cc
@@ -211,22 +211,25 @@ StackTrace::StackTrace() {
#pragma optimize("", on)
#endif
-StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
+StackTrace::StackTrace(const EXCEPTION_POINTERS* exception_pointers) {
// When walking an exception stack, we need to use StackWalk64().
count_ = 0;
+ // StackWalk64() may modify context record passed to it, so we will
+ // use a copy.
+ CONTEXT context_record = *exception_pointers->ContextRecord;
// 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;
+ stack_frame.AddrPC.Offset = context_record.Rip;
+ stack_frame.AddrFrame.Offset = context_record.Rbp;
+ stack_frame.AddrStack.Offset = context_record.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;
+ stack_frame.AddrPC.Offset = context_record.Eip;
+ stack_frame.AddrFrame.Offset = context_record.Ebp;
+ stack_frame.AddrStack.Offset = context_record.Esp;
#endif
stack_frame.AddrPC.Mode = AddrModeFlat;
stack_frame.AddrFrame.Mode = AddrModeFlat;
@@ -235,7 +238,7 @@ StackTrace::StackTrace(EXCEPTION_POINTERS* exception_pointers) {
GetCurrentProcess(),
GetCurrentThread(),
&stack_frame,
- exception_pointers->ContextRecord,
+ &context_record,
NULL,
&SymFunctionTableAccess64,
&SymGetModuleBase64,
diff --git a/chromium/base/debug/trace_event.h b/chromium/base/debug/trace_event.h
index 18feb33f3d0..686bd38790c 100644
--- a/chromium/base/debug/trace_event.h
+++ b/chromium/base/debug/trace_event.h
@@ -195,6 +195,7 @@
#include "base/debug/trace_event_impl.h"
#include "base/debug/trace_event_memory.h"
#include "base/debug/trace_event_system_stats_monitor.h"
+#include "base/time/time.h"
#include "build/build_config.h"
// By default, const char* argument values are assumed to have long-lived scope
@@ -725,12 +726,16 @@
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_DELETE_OBJECT, \
category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE)
+#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \
+ UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
+ (base::debug::TraceLog::ENABLED_FOR_RECORDING | \
+ base::debug::TraceLog::ENABLED_FOR_EVENT_CALLBACK))
// Macro to efficiently determine if a given category group is enabled.
#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
*ret = true; \
} else { \
*ret = false; \
@@ -850,7 +855,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
category_group_enabled = \
reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD( \
atomic)); \
- if (!category_group_enabled) { \
+ if (UNLIKELY(!category_group_enabled)) { \
category_group_enabled = \
TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \
TRACE_EVENT_API_ATOMIC_STORE(atomic, \
@@ -870,7 +875,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
trace_event_internal::AddTraceEvent( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \
@@ -883,7 +888,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
- if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
base::debug::TraceEventHandle h = trace_event_internal::AddTraceEvent( \
TRACE_EVENT_PHASE_COMPLETE, \
INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
@@ -899,7 +904,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
trace_event_internal::TraceID trace_event_trace_id( \
id, &trace_event_flags); \
@@ -916,7 +921,7 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
category_group, name, id, thread_id, timestamp, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
trace_event_internal::TraceID trace_event_trace_id( \
id, &trace_event_flags); \
@@ -1092,7 +1097,7 @@ union TraceValueUnion {
class TraceStringWithCopy {
public:
explicit TraceStringWithCopy(const char* str) : str_(str) {}
- operator const char* () const { return str_; }
+ const char* str() const { return str_; }
private:
const char* str_;
};
@@ -1101,6 +1106,7 @@ class TraceStringWithCopy {
// 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, \
+ arg_expression, \
union_member, \
value_type_id) \
static inline void SetTraceValue( \
@@ -1108,7 +1114,7 @@ class TraceStringWithCopy {
unsigned char* type, \
unsigned long long* value) { \
TraceValueUnion type_value; \
- type_value.union_member = arg; \
+ type_value.union_member = arg_expression; \
*type = value_type_id; \
*value = type_value.as_uint; \
}
@@ -1133,14 +1139,15 @@ 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,
+INTERNAL_DECLARE_SET_TRACE_VALUE(bool, arg, as_bool, TRACE_VALUE_TYPE_BOOL)
+INTERNAL_DECLARE_SET_TRACE_VALUE(double, arg, as_double,
+ TRACE_VALUE_TYPE_DOUBLE)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, arg, as_pointer,
TRACE_VALUE_TYPE_POINTER)
-INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string,
+INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, arg, as_string,
TRACE_VALUE_TYPE_STRING)
-INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string,
- TRACE_VALUE_TYPE_COPY_STRING)
+INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, arg.str(),
+ as_string, TRACE_VALUE_TYPE_COPY_STRING)
#undef INTERNAL_DECLARE_SET_TRACE_VALUE
#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT
@@ -1155,6 +1162,22 @@ static inline void SetTraceValue(const std::string& arg,
*value = type_value.as_uint;
}
+// base::Time and base::TimeTicks version of SetTraceValue to make it easier to
+// trace these types.
+static inline void SetTraceValue(const base::Time arg,
+ unsigned char* type,
+ unsigned long long* value) {
+ *type = TRACE_VALUE_TYPE_INT;
+ *value = arg.ToInternalValue();
+}
+
+static inline void SetTraceValue(const base::TimeTicks arg,
+ unsigned char* type,
+ unsigned long long* value) {
+ *type = TRACE_VALUE_TYPE_INT;
+ *value = arg.ToInternalValue();
+}
+
// These AddTraceEvent and AddTraceEventWithThreadIdAndTimestamp 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
diff --git a/chromium/base/debug/trace_event_android.cc b/chromium/base/debug/trace_event_android.cc
index 567c48eb293..ed6b20d0bf7 100644
--- a/chromium/base/debug/trace_event_android.cc
+++ b/chromium/base/debug/trace_event_android.cc
@@ -97,6 +97,7 @@ void TraceLog::StartATrace() {
return;
}
SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString),
+ base::debug::TraceLog::RECORDING_MODE,
RECORD_CONTINUOUSLY);
}
diff --git a/chromium/base/debug/trace_event_impl.cc b/chromium/base/debug/trace_event_impl.cc
index e774f621e04..8bcbe0e45ef 100644
--- a/chromium/base/debug/trace_event_impl.cc
+++ b/chromium/base/debug/trace_event_impl.cc
@@ -11,6 +11,8 @@
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
#include "base/debug/trace_event.h"
+#include "base/debug/trace_event_synthetic_delay.h"
+#include "base/float_util.h"
#include "base/format_macros.h"
#include "base/json/string_escape.h"
#include "base/lazy_instance.h"
@@ -69,8 +71,12 @@ const size_t kEchoToConsoleTraceEventBufferChunks = 256;
const int kThreadFlushTimeoutMs = 3000;
+#if !defined(OS_NACL)
// These categories will cause deadlock when ECHO_TO_CONSOLE. crbug.com/325575.
const char kEchoToConsoleCategoryFilter[] = "-ipc,-task";
+#endif
+
+const char kSyntheticDelayCategoryFilterPrefix[] = "DELAY(";
#define MAX_CATEGORY_GROUPS 100
@@ -81,6 +87,7 @@ const char kEchoToConsoleCategoryFilter[] = "-ipc,-task";
// convert internally to determine the category name from the char enabled
// pointer.
const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
+ "toplevel",
"tracing already shutdown",
"tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
"__metadata",
@@ -89,12 +96,14 @@ const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
// The enabled flag is char instead of bool so that the API can be used from C.
unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = { 0 };
-const int g_category_already_shutdown = 0;
-const int g_category_categories_exhausted = 1;
-const int g_category_metadata = 2;
-const int g_category_trace_event_overhead = 3;
-const int g_num_builtin_categories = 4;
-int g_category_index = g_num_builtin_categories; // Skip default categories.
+// Indexes here have to match the g_category_groups array indexes above.
+const int g_category_already_shutdown = 1;
+const int g_category_categories_exhausted = 2;
+const int g_category_metadata = 3;
+const int g_category_trace_event_overhead = 4;
+const int g_num_builtin_categories = 5;
+// Skip default categories.
+base::subtle::AtomicWord g_category_index = g_num_builtin_categories;
// The name of the current thread. This is used to decide if the current
// thread name has changed. We combine all the seen thread names into the
@@ -632,24 +641,35 @@ void TraceEvent::AppendValueAsJSON(unsigned char type,
case TRACE_VALUE_TYPE_DOUBLE: {
// FIXME: base/json/json_writer.cc is using the same code,
// should be made into a common method.
- std::string real = DoubleToString(value.as_double);
- // 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");
+ std::string real;
+ double val = value.as_double;
+ if (IsFinite(val)) {
+ real = DoubleToString(val);
+ // 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");
+ }
+ } else if (IsNaN(val)){
+ // The JSON spec doesn't allow NaN and Infinity (since these are
+ // objects in EcmaScript). Use strings instead.
+ real = "\"NaN\"";
+ } else if (val < 0) {
+ real = "\"-Infinity\"";
+ } else {
+ real = "\"Infinity\"";
}
-
StringAppendF(out, "%s", real.c_str());
break;
}
@@ -1094,7 +1114,7 @@ void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() {
trace_log_->lock_.AssertAcquired();
if (trace_log_->CheckGeneration(generation_)) {
- // Return the chunk to the buffer only if the generation matches,
+ // Return the chunk to the buffer only if the generation matches.
trace_log_->logged_events_->ReturnChunk(chunk_index_, chunk_.Pass());
}
// Otherwise this method may be called from the destructor, or TraceLog will
@@ -1107,7 +1127,7 @@ TraceLog* TraceLog::GetInstance() {
}
TraceLog::TraceLog()
- : enabled_(false),
+ : mode_(DISABLED),
num_traces_recorded_(0),
event_callback_(0),
dispatching_to_observer_list_(false),
@@ -1153,7 +1173,7 @@ TraceLog::TraceLog()
LOG(ERROR) << "Start " << switches::kTraceToConsole
<< " with CategoryFilter '" << filter << "'.";
- SetEnabled(CategoryFilter(filter), ECHO_TO_CONSOLE);
+ SetEnabled(CategoryFilter(filter), RECORDING_MODE, ECHO_TO_CONSOLE);
}
#endif
@@ -1192,8 +1212,12 @@ const char* TraceLog::GetCategoryGroupName(
void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) {
unsigned char enabled_flag = 0;
const char* category_group = g_category_groups[category_index];
- if (enabled_ && category_filter_.IsCategoryGroupEnabled(category_group))
+ if (mode_ == RECORDING_MODE &&
+ category_filter_.IsCategoryGroupEnabled(category_group))
enabled_flag |= ENABLED_FOR_RECORDING;
+ else if (mode_ == MONITORING_MODE &&
+ category_filter_.IsCategoryGroupEnabled(category_group))
+ enabled_flag |= ENABLED_FOR_MONITORING;
if (event_callback_ &&
event_callback_category_filter_.IsCategoryGroupEnabled(category_group))
enabled_flag |= ENABLED_FOR_EVENT_CALLBACK;
@@ -1201,48 +1225,89 @@ void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) {
}
void TraceLog::UpdateCategoryGroupEnabledFlags() {
- for (int i = 0; i < g_category_index; i++)
+ int category_index = base::subtle::NoBarrier_Load(&g_category_index);
+ for (int i = 0; i < category_index; i++)
UpdateCategoryGroupEnabledFlag(i);
}
+void TraceLog::UpdateSyntheticDelaysFromCategoryFilter() {
+ ResetTraceEventSyntheticDelays();
+ const CategoryFilter::StringList& delays =
+ category_filter_.GetSyntheticDelayValues();
+ CategoryFilter::StringList::const_iterator ci;
+ for (ci = delays.begin(); ci != delays.end(); ++ci) {
+ StringTokenizer tokens(*ci, ";");
+ if (!tokens.GetNext())
+ continue;
+ TraceEventSyntheticDelay* delay =
+ TraceEventSyntheticDelay::Lookup(tokens.token());
+ while (tokens.GetNext()) {
+ std::string token = tokens.token();
+ char* duration_end;
+ double target_duration = strtod(token.c_str(), &duration_end);
+ if (duration_end != token.c_str()) {
+ delay->SetTargetDuration(
+ TimeDelta::FromMicroseconds(target_duration * 1e6));
+ } else if (token == "static") {
+ delay->SetMode(TraceEventSyntheticDelay::STATIC);
+ } else if (token == "oneshot") {
+ delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT);
+ } else if (token == "alternating") {
+ delay->SetMode(TraceEventSyntheticDelay::ALTERNATING);
+ }
+ }
+ }
+}
+
const unsigned char* TraceLog::GetCategoryGroupEnabledInternal(
const char* category_group) {
DCHECK(!strchr(category_group, '"')) <<
"Category groups may not contain double quote";
- AutoLock lock(lock_);
+ // The g_category_groups is append only, avoid using a lock for the fast path.
+ int current_category_index = base::subtle::Acquire_Load(&g_category_index);
- unsigned char* category_group_enabled = NULL;
// Search for pre-existing category group.
- for (int i = 0; i < g_category_index; i++) {
+ for (int i = 0; i < current_category_index; ++i) {
if (strcmp(g_category_groups[i], category_group) == 0) {
- category_group_enabled = &g_category_group_enabled[i];
- break;
+ return &g_category_group_enabled[i];
}
}
- if (!category_group_enabled) {
- // Create a new category group
- DCHECK(g_category_index < MAX_CATEGORY_GROUPS) <<
- "must increase MAX_CATEGORY_GROUPS";
- if (g_category_index < MAX_CATEGORY_GROUPS) {
- int new_index = g_category_index++;
- // Don't hold on to the category_group pointer, so that we can create
- // category groups with strings not known at compile time (this is
- // required by SetWatchEvent).
- const char* new_group = strdup(category_group);
- ANNOTATE_LEAKING_OBJECT_PTR(new_group);
- g_category_groups[new_index] = new_group;
- DCHECK(!g_category_group_enabled[new_index]);
- // Note that if both included and excluded patterns in the
- // CategoryFilter are empty, we exclude nothing,
- // thereby enabling this category group.
- UpdateCategoryGroupEnabledFlag(new_index);
- category_group_enabled = &g_category_group_enabled[new_index];
- } else {
- category_group_enabled =
- &g_category_group_enabled[g_category_categories_exhausted];
+ unsigned char* category_group_enabled = NULL;
+ // This is the slow path: the lock is not held in the case above, so more
+ // than one thread could have reached here trying to add the same category.
+ // Only hold to lock when actually appending a new category, and
+ // check the categories groups again.
+ AutoLock lock(lock_);
+ int category_index = base::subtle::Acquire_Load(&g_category_index);
+ for (int i = 0; i < category_index; ++i) {
+ if (strcmp(g_category_groups[i], category_group) == 0) {
+ return &g_category_group_enabled[i];
}
}
+
+ // Create a new category group.
+ DCHECK(category_index < MAX_CATEGORY_GROUPS) <<
+ "must increase MAX_CATEGORY_GROUPS";
+ if (category_index < MAX_CATEGORY_GROUPS) {
+ // Don't hold on to the category_group pointer, so that we can create
+ // category groups with strings not known at compile time (this is
+ // required by SetWatchEvent).
+ const char* new_group = strdup(category_group);
+ ANNOTATE_LEAKING_OBJECT_PTR(new_group);
+ g_category_groups[category_index] = new_group;
+ DCHECK(!g_category_group_enabled[category_index]);
+ // Note that if both included and excluded patterns in the
+ // CategoryFilter are empty, we exclude nothing,
+ // thereby enabling this category group.
+ UpdateCategoryGroupEnabledFlag(category_index);
+ category_group_enabled = &g_category_group_enabled[category_index];
+ // Update the max index now.
+ base::subtle::Release_Store(&g_category_index, category_index + 1);
+ } else {
+ category_group_enabled =
+ &g_category_group_enabled[g_category_categories_exhausted];
+ }
return category_group_enabled;
}
@@ -1251,11 +1316,13 @@ void TraceLog::GetKnownCategoryGroups(
AutoLock lock(lock_);
category_groups->push_back(
g_category_groups[g_category_trace_event_overhead]);
- for (int i = g_num_builtin_categories; i < g_category_index; i++)
+ int category_index = base::subtle::NoBarrier_Load(&g_category_index);
+ for (int i = g_num_builtin_categories; i < category_index; i++)
category_groups->push_back(g_category_groups[i]);
}
void TraceLog::SetEnabled(const CategoryFilter& category_filter,
+ Mode mode,
Options options) {
std::vector<EnabledStateObserver*> observer_list;
{
@@ -1266,12 +1333,16 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
Options old_options = trace_options();
- if (enabled_) {
+ if (IsEnabled()) {
if (options != old_options) {
- DLOG(ERROR) << "Attemting to re-enable tracing with a different "
+ DLOG(ERROR) << "Attempting to re-enable tracing with a different "
<< "set of options.";
}
+ if (mode != mode_) {
+ DLOG(ERROR) << "Attempting to re-enable tracing with a different mode.";
+ }
+
category_filter_.Merge(category_filter);
UpdateCategoryGroupEnabledFlags();
return;
@@ -1283,7 +1354,7 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
return;
}
- enabled_ = true;
+ mode_ = mode;
if (options != old_options) {
subtle::NoBarrier_Store(&trace_options_, options);
@@ -1294,8 +1365,9 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
category_filter_ = CategoryFilter(category_filter);
UpdateCategoryGroupEnabledFlags();
+ UpdateSyntheticDelaysFromCategoryFilter();
- if ((options & ENABLE_SAMPLING) || (options & MONITOR_SAMPLING)) {
+ if (options & ENABLE_SAMPLING) {
sampling_thread_.reset(new TraceSamplingThread);
sampling_thread_->RegisterSampleBucket(
&g_trace_state[0],
@@ -1341,7 +1413,7 @@ void TraceLog::SetDisabled() {
void TraceLog::SetDisabledWhileLocked() {
lock_.AssertAcquired();
- if (!enabled_)
+ if (!IsEnabled())
return;
if (dispatching_to_observer_list_) {
@@ -1350,7 +1422,7 @@ void TraceLog::SetDisabledWhileLocked() {
return;
}
- enabled_ = false;
+ mode_ = DISABLED;
if (sampling_thread_.get()) {
// Stop the sampling thread.
@@ -1384,7 +1456,7 @@ void TraceLog::SetDisabledWhileLocked() {
int TraceLog::GetNumTracesRecorded() {
AutoLock lock(lock_);
- if (!enabled_)
+ if (!IsEnabled())
return -1;
return num_traces_recorded_;
}
@@ -1425,7 +1497,7 @@ TraceBuffer* TraceLog::CreateTraceBuffer() {
Options options = trace_options();
if (options & RECORD_CONTINUOUSLY)
return new TraceBufferRingBuffer(kTraceEventRingBufferChunks);
- else if (options & MONITOR_SAMPLING)
+ else if ((options & ENABLE_SAMPLING) && mode_ == MONITORING_MODE)
return new TraceBufferRingBuffer(kMonitorTraceEventBufferChunks);
else if (options & ECHO_TO_CONSOLE)
return new TraceBufferRingBuffer(kEchoToConsoleTraceEventBufferChunks);
@@ -1769,7 +1841,8 @@ TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
}
std::string console_message;
- if ((*category_group_enabled & ENABLED_FOR_RECORDING)) {
+ if (*category_group_enabled &
+ (ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) {
OptionalAutoLock lock(lock_);
TraceEvent* trace_event = NULL;
@@ -1898,8 +1971,7 @@ void TraceLog::AddTraceEventEtw(char phase,
void TraceLog::AddTraceEventEtw(char phase,
const char* name,
const void* id,
- const std::string& extra)
-{
+ const std::string& extra) {
#if defined(OS_WIN)
TraceEventETWProvider::Trace(name, phase, id, extra);
#endif
@@ -1977,6 +2049,14 @@ void TraceLog::CancelWatchEvent() {
void TraceLog::AddMetadataEventsWhileLocked() {
lock_.AssertAcquired();
+#if !defined(OS_NACL) // NaCl shouldn't expose the process id.
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ 0,
+ "num_cpus", "number",
+ base::SysInfo::NumberOfProcessors());
+#endif
+
+
int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId());
if (process_sort_index_ != 0) {
InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
@@ -2166,7 +2246,8 @@ CategoryFilter::CategoryFilter(const std::string& filter_string) {
CategoryFilter::CategoryFilter(const CategoryFilter& cf)
: included_(cf.included_),
disabled_(cf.disabled_),
- excluded_(cf.excluded_) {
+ excluded_(cf.excluded_),
+ delays_(cf.delays_) {
}
CategoryFilter::~CategoryFilter() {
@@ -2179,6 +2260,7 @@ CategoryFilter& CategoryFilter::operator=(const CategoryFilter& rhs) {
included_ = rhs.included_;
disabled_ = rhs.disabled_;
excluded_ = rhs.excluded_;
+ delays_ = rhs.delays_;
return *this;
}
@@ -2191,8 +2273,19 @@ void CategoryFilter::Initialize(const std::string& filter_string) {
// Ignore empty categories.
if (category.empty())
continue;
- // Excluded categories start with '-'.
- if (category.at(0) == '-') {
+ // Synthetic delays are of the form 'DELAY(delay;option;option;...)'.
+ if (category.find(kSyntheticDelayCategoryFilterPrefix) == 0 &&
+ category.at(category.size() - 1) == ')') {
+ category = category.substr(
+ strlen(kSyntheticDelayCategoryFilterPrefix),
+ category.size() - strlen(kSyntheticDelayCategoryFilterPrefix) - 1);
+ size_t name_length = category.find(';');
+ if (name_length != std::string::npos && name_length > 0 &&
+ name_length != category.size() - 1) {
+ delays_.push_back(category);
+ }
+ } else if (category.at(0) == '-') {
+ // Excluded categories start with '-'.
// Remove '-' from category string.
category = category.substr(1);
excluded_.push_back(category);
@@ -2219,11 +2312,26 @@ void CategoryFilter::WriteString(const StringList& values,
}
}
+void CategoryFilter::WriteString(const StringList& delays,
+ std::string* out) const {
+ bool prepend_comma = !out->empty();
+ int token_cnt = 0;
+ for (StringList::const_iterator ci = delays.begin();
+ ci != delays.end(); ++ci) {
+ if (token_cnt > 0 || prepend_comma)
+ StringAppendF(out, ",");
+ StringAppendF(out, "%s%s)", kSyntheticDelayCategoryFilterPrefix,
+ ci->c_str());
+ ++token_cnt;
+ }
+}
+
std::string CategoryFilter::ToString() const {
std::string filter_string;
WriteString(included_, &filter_string, true);
WriteString(disabled_, &filter_string, true);
WriteString(excluded_, &filter_string, false);
+ WriteString(delays_, &filter_string);
return filter_string;
}
@@ -2279,6 +2387,9 @@ void CategoryFilter::Merge(const CategoryFilter& nested_filter) {
excluded_.insert(excluded_.end(),
nested_filter.excluded_.begin(),
nested_filter.excluded_.end());
+ delays_.insert(delays_.end(),
+ nested_filter.delays_.begin(),
+ nested_filter.delays_.end());
}
void CategoryFilter::Clear() {
@@ -2287,6 +2398,11 @@ void CategoryFilter::Clear() {
excluded_.clear();
}
+const CategoryFilter::StringList&
+ CategoryFilter::GetSyntheticDelayValues() const {
+ return delays_;
+}
+
} // namespace debug
} // namespace base
diff --git a/chromium/base/debug/trace_event_impl.h b/chromium/base/debug/trace_event_impl.h
index 61794807a41..e5a6dbd71f9 100644
--- a/chromium/base/debug/trace_event_impl.h
+++ b/chromium/base/debug/trace_event_impl.h
@@ -125,10 +125,6 @@ class BASE_EXPORT TraceEvent {
void UpdateDuration(const TimeTicks& now, const TimeTicks& thread_now);
// 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;
void AppendPrettyPrinted(std::ostringstream* out) const;
@@ -283,6 +279,8 @@ class BASE_EXPORT TraceResultBuffer {
class BASE_EXPORT CategoryFilter {
public:
+ typedef std::vector<std::string> StringList;
+
// The default category filter, used when none is provided.
// Allows all categories through, except if they end in the suffix 'Debug' or
// 'Test'.
@@ -298,6 +296,17 @@ class BASE_EXPORT CategoryFilter {
// Example: CategoryFilter("-excluded_category1,-excluded_category2");
// Example: CategoryFilter("-*,webkit"); would disable everything but webkit.
// Example: CategoryFilter("-webkit"); would enable everything but webkit.
+ //
+ // Category filters can also be used to configure synthetic delays.
+ //
+ // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16)"); would make swap
+ // buffers always take at least 16 ms.
+ // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16;oneshot)"); would
+ // make swap buffers take at least 16 ms the first time it is
+ // called.
+ // Example: CategoryFilter("DELAY(gpu.PresentingFrame;16;alternating)");
+ // would make swap buffers take at least 16 ms every other time it
+ // is called.
explicit CategoryFilter(const std::string& filter_string);
CategoryFilter(const CategoryFilter& cf);
@@ -317,6 +326,9 @@ class BASE_EXPORT CategoryFilter {
// disabled by this category filter.
bool IsCategoryGroupEnabled(const char* category_group) const;
+ // Return a list of the synthetic delays specified in this category filter.
+ const StringList& GetSyntheticDelayValues() const;
+
// Merges nested_filter with the current CategoryFilter
void Merge(const CategoryFilter& nested_filter);
@@ -334,12 +346,11 @@ class BASE_EXPORT CategoryFilter {
static bool IsEmptyOrContainsLeadingOrTrailingWhitespace(
const std::string& str);
- typedef std::vector<std::string> StringList;
-
void Initialize(const std::string& filter_string);
void WriteString(const StringList& values,
std::string* out,
bool included) const;
+ void WriteString(const StringList& delays, std::string* out) const;
bool HasIncludedPatterns() const;
bool DoesCategoryGroupContainCategory(const char* category_group,
@@ -348,12 +359,19 @@ class BASE_EXPORT CategoryFilter {
StringList included_;
StringList disabled_;
StringList excluded_;
+ StringList delays_;
};
class TraceSamplingThread;
class BASE_EXPORT TraceLog {
public:
+ enum Mode {
+ DISABLED = 0,
+ RECORDING_MODE,
+ MONITORING_MODE,
+ };
+
// Options determines how the trace buffer stores data.
enum Options {
// Record until the trace buffer is full.
@@ -366,21 +384,21 @@ class BASE_EXPORT TraceLog {
// Enable the sampling profiler in the recording mode.
ENABLE_SAMPLING = 1 << 2,
- // Enable the sampling profiler in the monitoring mode.
- MONITOR_SAMPLING = 1 << 3,
-
// Echo to console. Events are discarded.
- ECHO_TO_CONSOLE = 1 << 4,
+ ECHO_TO_CONSOLE = 1 << 3,
};
// The pointer returned from GetCategoryGroupEnabledInternal() 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.
+ // These values must be in sync with macro values in TraceEvent.h in Blink.
enum CategoryGroupEnabledFlags {
- // Normal enabled flag for category groups enabled by SetEnabled().
+ // Category group enabled for the recording mode.
ENABLED_FOR_RECORDING = 1 << 0,
+ // Category group enabled for the monitoring mode.
+ ENABLED_FOR_MONITORING = 1 << 1,
// Category group enabled by SetEventCallbackEnabled().
- ENABLED_FOR_EVENT_CALLBACK = 1 << 1,
+ ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
};
static TraceLog* GetInstance();
@@ -400,12 +418,13 @@ class BASE_EXPORT TraceLog {
// See CategoryFilter comments for details on how to control what categories
// will be traced. If tracing has already been enabled, |category_filter| will
// be merged into the current category filter.
- void SetEnabled(const CategoryFilter& category_filter, Options options);
+ void SetEnabled(const CategoryFilter& category_filter,
+ Mode mode, Options options);
// Disables normal tracing for all categories.
void SetDisabled();
- bool IsEnabled() { return enabled_; }
+ bool IsEnabled() { return mode_ != DISABLED; }
// The number of times we have begun recording traces. If tracing is off,
// returns -1. If tracing is on, then it returns the number of times we have
@@ -426,7 +445,7 @@ class BASE_EXPORT TraceLog {
class EnabledStateObserver {
public:
// Called just after the tracing system becomes enabled, outside of the
- // |lock_|. TraceLog::IsEnabled() is true at this point.
+ // |lock_|. TraceLog::IsEnabled() is true at this point.
virtual void OnTraceLogEnabled() = 0;
// Called just after the tracing system disables, outside of the |lock_|.
@@ -591,14 +610,18 @@ class BASE_EXPORT TraceLog {
// by the Singleton class.
friend struct DefaultSingletonTraits<TraceLog>;
- // Enable/disable each category group based on the current enabled_,
+ // Enable/disable each category group based on the current mode_,
// category_filter_, event_callback_ and event_callback_category_filter_.
- // Enable the category group if enabled_ is true and category_filter_ matches
+ // Enable the category group in the enabled mode if category_filter_ matches
// the category group, or event_callback_ is not null and
// event_callback_category_filter_ matches the category group.
void UpdateCategoryGroupEnabledFlags();
void UpdateCategoryGroupEnabledFlag(int category_index);
+ // Configure synthetic delays based on the values set in the current
+ // category filter.
+ void UpdateSyntheticDelaysFromCategoryFilter();
+
class ThreadLocalEventBuffer;
class OptionalAutoLock;
@@ -652,7 +675,7 @@ class BASE_EXPORT TraceLog {
// and thread_colors_.
Lock thread_info_lock_;
int locked_line_;
- bool enabled_;
+ Mode mode_;
int num_traces_recorded_;
scoped_ptr<TraceBuffer> logged_events_;
subtle::AtomicWord /* EventCallback */ event_callback_;
diff --git a/chromium/base/debug/trace_event_memory.cc b/chromium/base/debug/trace_event_memory.cc
index 4b2b050df8e..3c468278a15 100644
--- a/chromium/base/debug/trace_event_memory.cc
+++ b/chromium/base/debug/trace_event_memory.cc
@@ -409,9 +409,9 @@ bool AppendHeapProfileLineAsTraceFormat(const std::string& line,
// TODO(jamescook): Report the trace category and name separately to the
// trace viewer and allow it to decide what decorations to apply. For now
- // just hard-code a decoration for posted tasks.
+ // just hard-code a decoration for posted tasks (toplevel).
std::string trace_string(trace_name);
- if (!strcmp(trace_category, "task"))
+ if (!strcmp(trace_category, "toplevel"))
trace_string.append("->PostTask");
// Some trace name strings have double quotes, convert them to single.
diff --git a/chromium/base/debug/trace_event_memory_unittest.cc b/chromium/base/debug/trace_event_memory_unittest.cc
index c0a1568e357..3f5cad3ede2 100644
--- a/chromium/base/debug/trace_event_memory_unittest.cc
+++ b/chromium/base/debug/trace_event_memory_unittest.cc
@@ -164,10 +164,10 @@ TEST_F(TraceMemoryTest, AppendHeapProfileLineAsTraceFormat) {
AppendHeapProfileLineAsTraceFormat(input.str().c_str(), &output));
EXPECT_EQ(kExpectedOutput, output);
- // Input with with the category "task".
+ // Input with with the category "toplevel".
// TODO(jamescook): Eliminate this special case and move the logic to the
// trace viewer code.
- const char kTaskCategory[] = "task";
+ const char kTaskCategory[] = "toplevel";
const char kTaskName[] = "TaskName";
std::ostringstream input2;
input2 << " 68: 4195 [ 1087: 98009] @ " << &kTaskCategory << " "
diff --git a/chromium/base/debug/trace_event_synthetic_delay.cc b/chromium/base/debug/trace_event_synthetic_delay.cc
new file mode 100644
index 00000000000..ab1880941ba
--- /dev/null
+++ b/chromium/base/debug/trace_event_synthetic_delay.cc
@@ -0,0 +1,233 @@
+// Copyright 2014 The Chromium Authors. All 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_synthetic_delay.h"
+#include "base/memory/singleton.h"
+
+namespace {
+const int kMaxSyntheticDelays = 32;
+} // namespace
+
+namespace base {
+namespace debug {
+
+TraceEventSyntheticDelayClock::TraceEventSyntheticDelayClock() {}
+TraceEventSyntheticDelayClock::~TraceEventSyntheticDelayClock() {}
+
+class TraceEventSyntheticDelayRegistry : public TraceEventSyntheticDelayClock {
+ public:
+ static TraceEventSyntheticDelayRegistry* GetInstance();
+
+ TraceEventSyntheticDelay* GetOrCreateDelay(const char* name);
+ void ResetAllDelays();
+
+ // TraceEventSyntheticDelayClock implementation.
+ virtual base::TimeTicks Now() OVERRIDE;
+
+ private:
+ TraceEventSyntheticDelayRegistry();
+
+ friend struct DefaultSingletonTraits<TraceEventSyntheticDelayRegistry>;
+
+ Lock lock_;
+ TraceEventSyntheticDelay delays_[kMaxSyntheticDelays];
+ TraceEventSyntheticDelay dummy_delay_;
+ base::subtle::Atomic32 delay_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayRegistry);
+};
+
+TraceEventSyntheticDelay::TraceEventSyntheticDelay()
+ : mode_(STATIC), begin_count_(0), trigger_count_(0), clock_(NULL) {}
+
+TraceEventSyntheticDelay::~TraceEventSyntheticDelay() {}
+
+TraceEventSyntheticDelay* TraceEventSyntheticDelay::Lookup(
+ const std::string& name) {
+ return TraceEventSyntheticDelayRegistry::GetInstance()->GetOrCreateDelay(
+ name.c_str());
+}
+
+void TraceEventSyntheticDelay::Initialize(
+ const std::string& name,
+ TraceEventSyntheticDelayClock* clock) {
+ name_ = name;
+ clock_ = clock;
+}
+
+void TraceEventSyntheticDelay::SetTargetDuration(
+ base::TimeDelta target_duration) {
+ AutoLock lock(lock_);
+ target_duration_ = target_duration;
+ trigger_count_ = 0;
+ begin_count_ = 0;
+}
+
+void TraceEventSyntheticDelay::SetMode(Mode mode) {
+ AutoLock lock(lock_);
+ mode_ = mode;
+}
+
+void TraceEventSyntheticDelay::SetClock(TraceEventSyntheticDelayClock* clock) {
+ AutoLock lock(lock_);
+ clock_ = clock;
+}
+
+void TraceEventSyntheticDelay::Begin() {
+ // Note that we check for a non-zero target duration without locking to keep
+ // things quick for the common case when delays are disabled. Since the delay
+ // calculation is done with a lock held, it will always be correct. The only
+ // downside of this is that we may fail to apply some delays when the target
+ // duration changes.
+ ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
+ if (!target_duration_.ToInternalValue())
+ return;
+
+ base::TimeTicks start_time = clock_->Now();
+ {
+ AutoLock lock(lock_);
+ if (++begin_count_ != 1)
+ return;
+ end_time_ = CalculateEndTimeLocked(start_time);
+ }
+}
+
+void TraceEventSyntheticDelay::BeginParallel(base::TimeTicks* out_end_time) {
+ // See note in Begin().
+ ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
+ if (!target_duration_.ToInternalValue()) {
+ *out_end_time = base::TimeTicks();
+ return;
+ }
+
+ base::TimeTicks start_time = clock_->Now();
+ {
+ AutoLock lock(lock_);
+ *out_end_time = CalculateEndTimeLocked(start_time);
+ }
+}
+
+void TraceEventSyntheticDelay::End() {
+ // See note in Begin().
+ ANNOTATE_BENIGN_RACE(&target_duration_, "Synthetic delay duration");
+ if (!target_duration_.ToInternalValue())
+ return;
+
+ base::TimeTicks end_time;
+ {
+ AutoLock lock(lock_);
+ if (!begin_count_ || --begin_count_ != 0)
+ return;
+ end_time = end_time_;
+ }
+ if (!end_time.is_null())
+ ApplyDelay(end_time);
+}
+
+void TraceEventSyntheticDelay::EndParallel(base::TimeTicks end_time) {
+ if (!end_time.is_null())
+ ApplyDelay(end_time);
+}
+
+base::TimeTicks TraceEventSyntheticDelay::CalculateEndTimeLocked(
+ base::TimeTicks start_time) {
+ if (mode_ == ONE_SHOT && trigger_count_++)
+ return base::TimeTicks();
+ else if (mode_ == ALTERNATING && trigger_count_++ % 2)
+ return base::TimeTicks();
+ return start_time + target_duration_;
+}
+
+void TraceEventSyntheticDelay::ApplyDelay(base::TimeTicks end_time) {
+ TRACE_EVENT0("synthetic_delay", name_.c_str());
+ while (clock_->Now() < end_time) {
+ // Busy loop.
+ }
+}
+
+TraceEventSyntheticDelayRegistry*
+TraceEventSyntheticDelayRegistry::GetInstance() {
+ return Singleton<
+ TraceEventSyntheticDelayRegistry,
+ LeakySingletonTraits<TraceEventSyntheticDelayRegistry> >::get();
+}
+
+TraceEventSyntheticDelayRegistry::TraceEventSyntheticDelayRegistry()
+ : delay_count_(0) {}
+
+TraceEventSyntheticDelay* TraceEventSyntheticDelayRegistry::GetOrCreateDelay(
+ const char* name) {
+ // Try to find an existing delay first without locking to make the common case
+ // fast.
+ int delay_count = base::subtle::Acquire_Load(&delay_count_);
+ for (int i = 0; i < delay_count; ++i) {
+ if (!strcmp(name, delays_[i].name_.c_str()))
+ return &delays_[i];
+ }
+
+ AutoLock lock(lock_);
+ delay_count = base::subtle::Acquire_Load(&delay_count_);
+ for (int i = 0; i < delay_count; ++i) {
+ if (!strcmp(name, delays_[i].name_.c_str()))
+ return &delays_[i];
+ }
+
+ DCHECK(delay_count < kMaxSyntheticDelays)
+ << "must increase kMaxSyntheticDelays";
+ if (delay_count >= kMaxSyntheticDelays)
+ return &dummy_delay_;
+
+ delays_[delay_count].Initialize(std::string(name), this);
+ base::subtle::Release_Store(&delay_count_, delay_count + 1);
+ return &delays_[delay_count];
+}
+
+base::TimeTicks TraceEventSyntheticDelayRegistry::Now() {
+ return base::TimeTicks::HighResNow();
+}
+
+void TraceEventSyntheticDelayRegistry::ResetAllDelays() {
+ AutoLock lock(lock_);
+ int delay_count = base::subtle::Acquire_Load(&delay_count_);
+ for (int i = 0; i < delay_count; ++i) {
+ delays_[i].SetTargetDuration(base::TimeDelta());
+ delays_[i].SetClock(this);
+ }
+}
+
+void ResetTraceEventSyntheticDelays() {
+ TraceEventSyntheticDelayRegistry::GetInstance()->ResetAllDelays();
+}
+
+} // namespace debug
+} // namespace base
+
+namespace trace_event_internal {
+
+ScopedSyntheticDelay::ScopedSyntheticDelay(const char* name,
+ base::subtle::AtomicWord* impl_ptr)
+ : delay_impl_(GetOrCreateDelay(name, impl_ptr)) {
+ delay_impl_->BeginParallel(&end_time_);
+}
+
+ScopedSyntheticDelay::~ScopedSyntheticDelay() {
+ delay_impl_->EndParallel(end_time_);
+}
+
+base::debug::TraceEventSyntheticDelay* GetOrCreateDelay(
+ const char* name,
+ base::subtle::AtomicWord* impl_ptr) {
+ base::debug::TraceEventSyntheticDelay* delay_impl =
+ reinterpret_cast<base::debug::TraceEventSyntheticDelay*>(
+ base::subtle::NoBarrier_Load(impl_ptr));
+ if (!delay_impl) {
+ delay_impl = base::debug::TraceEventSyntheticDelayRegistry::GetInstance()
+ ->GetOrCreateDelay(name);
+ base::subtle::NoBarrier_Store(
+ impl_ptr, reinterpret_cast<base::subtle::AtomicWord>(delay_impl));
+ }
+ return delay_impl;
+}
+
+} // namespace trace_event_internal
diff --git a/chromium/base/debug/trace_event_synthetic_delay.h b/chromium/base/debug/trace_event_synthetic_delay.h
new file mode 100644
index 00000000000..06d6cdea1f9
--- /dev/null
+++ b/chromium/base/debug/trace_event_synthetic_delay.h
@@ -0,0 +1,166 @@
+// Copyright 2014 The Chromium 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 synthetic delay framework makes it possible to dynamically inject
+// arbitrary delays into into different parts of the codebase. This can be used,
+// for instance, for testing various task scheduling algorithms.
+//
+// The delays are specified in terms of a target duration for a given block of
+// code. If the code executes faster than the duration, the thread is made to
+// sleep until the deadline is met.
+//
+// Code can be instrumented for delays with two sets of macros. First, for
+// delays that should apply within a scope, use the following macro:
+//
+// TRACE_EVENT_SYNTHETIC_DELAY("cc.LayerTreeHost.DrawAndSwap");
+//
+// For delaying operations that span multiple scopes, use:
+//
+// TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("cc.Scheduler.BeginMainFrame");
+// ...
+// TRACE_EVENT_SYNTHETIC_DELAY_END("cc.Scheduler.BeginMainFrame");
+//
+// Here BEGIN establishes the start time for the delay and END executes the
+// delay based on the remaining time. If BEGIN is called multiple times in a
+// row, END should be called a corresponding number of times. Only the last
+// call to END will have an effect.
+//
+// Note that a single delay may begin on one thread and end on another. This
+// implies that a single delay cannot not be applied in several threads at once.
+
+#ifndef BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_
+#define BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_
+
+#include "base/atomicops.h"
+#include "base/debug/trace_event.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+
+// Apply a named delay in the current scope.
+#define TRACE_EVENT_SYNTHETIC_DELAY(name) \
+ static base::subtle::AtomicWord INTERNAL_TRACE_EVENT_UID(impl_ptr) = 0; \
+ trace_event_internal::ScopedSyntheticDelay INTERNAL_TRACE_EVENT_UID(delay)( \
+ name, &INTERNAL_TRACE_EVENT_UID(impl_ptr));
+
+// Begin a named delay, establishing its timing start point. May be called
+// multiple times as long as the calls to TRACE_EVENT_SYNTHETIC_DELAY_END are
+// balanced. Only the first call records the timing start point.
+#define TRACE_EVENT_SYNTHETIC_DELAY_BEGIN(name) \
+ do { \
+ static base::subtle::AtomicWord impl_ptr = 0; \
+ trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->Begin(); \
+ } while (false)
+
+// End a named delay. The delay is applied only if this call matches the
+// first corresponding call to TRACE_EVENT_SYNTHETIC_DELAY_BEGIN with the
+// same delay.
+#define TRACE_EVENT_SYNTHETIC_DELAY_END(name) \
+ do { \
+ static base::subtle::AtomicWord impl_ptr = 0; \
+ trace_event_internal::GetOrCreateDelay(name, &impl_ptr)->End(); \
+ } while (false)
+
+template <typename Type>
+struct DefaultSingletonTraits;
+
+namespace base {
+namespace debug {
+
+// Time source for computing delay durations. Used for testing.
+class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelayClock {
+ public:
+ TraceEventSyntheticDelayClock();
+ virtual ~TraceEventSyntheticDelayClock();
+ virtual base::TimeTicks Now() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayClock);
+};
+
+// Single delay point instance.
+class TRACE_EVENT_API_CLASS_EXPORT TraceEventSyntheticDelay {
+ public:
+ enum Mode {
+ STATIC, // Apply the configured delay every time.
+ ONE_SHOT, // Apply the configured delay just once.
+ ALTERNATING // Apply the configured delay every other time.
+ };
+
+ // Returns an existing named delay instance or creates a new one with |name|.
+ static TraceEventSyntheticDelay* Lookup(const std::string& name);
+
+ void SetTargetDuration(TimeDelta target_duration);
+ void SetMode(Mode mode);
+ void SetClock(TraceEventSyntheticDelayClock* clock);
+
+ // Begin the delay, establishing its timing start point. May be called
+ // multiple times as long as the calls to End() are balanced. Only the first
+ // call records the timing start point.
+ void Begin();
+
+ // End the delay. The delay is applied only if this call matches the first
+ // corresponding call to Begin() with the same delay.
+ void End();
+
+ // Begin a parallel instance of the delay. Several parallel instances may be
+ // active simultaneously and will complete independently. The computed end
+ // time for the delay is stored in |out_end_time|, which should later be
+ // passed to EndParallel().
+ void BeginParallel(base::TimeTicks* out_end_time);
+
+ // End a previously started parallel delay. |end_time| is the delay end point
+ // computed by BeginParallel().
+ void EndParallel(base::TimeTicks end_time);
+
+ private:
+ TraceEventSyntheticDelay();
+ ~TraceEventSyntheticDelay();
+ friend class TraceEventSyntheticDelayRegistry;
+
+ void Initialize(const std::string& name,
+ TraceEventSyntheticDelayClock* clock);
+ base::TimeTicks CalculateEndTimeLocked(base::TimeTicks start_time);
+ void ApplyDelay(base::TimeTicks end_time);
+
+ Lock lock_;
+ Mode mode_;
+ std::string name_;
+ int begin_count_;
+ int trigger_count_;
+ base::TimeTicks end_time_;
+ base::TimeDelta target_duration_;
+ TraceEventSyntheticDelayClock* clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelay);
+};
+
+// Set the target durations of all registered synthetic delay points to zero.
+TRACE_EVENT_API_CLASS_EXPORT void ResetTraceEventSyntheticDelays();
+
+} // namespace debug
+} // namespace base
+
+namespace trace_event_internal {
+
+// Helper class for scoped delays. Do not use directly.
+class TRACE_EVENT_API_CLASS_EXPORT ScopedSyntheticDelay {
+ public:
+ explicit ScopedSyntheticDelay(const char* name,
+ base::subtle::AtomicWord* impl_ptr);
+ ~ScopedSyntheticDelay();
+
+ private:
+ base::debug::TraceEventSyntheticDelay* delay_impl_;
+ base::TimeTicks end_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSyntheticDelay);
+};
+
+// Helper for registering delays. Do not use directly.
+TRACE_EVENT_API_CLASS_EXPORT base::debug::TraceEventSyntheticDelay*
+ GetOrCreateDelay(const char* name, base::subtle::AtomicWord* impl_ptr);
+
+} // namespace trace_event_internal
+
+#endif /* BASE_DEBUG_TRACE_EVENT_SYNTHETIC_DELAY_H_ */
diff --git a/chromium/base/debug/trace_event_synthetic_delay_unittest.cc b/chromium/base/debug/trace_event_synthetic_delay_unittest.cc
new file mode 100644
index 00000000000..7833e7bbcde
--- /dev/null
+++ b/chromium/base/debug/trace_event_synthetic_delay_unittest.cc
@@ -0,0 +1,156 @@
+// Copyright 2014 The Chromium Authors. All 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_synthetic_delay.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace debug {
+namespace {
+
+const int kTargetDurationMs = 100;
+// Allow some leeway in timings to make it possible to run these tests with a
+// wall clock time source too.
+const int kShortDurationMs = 10;
+
+} // namespace
+
+class TraceEventSyntheticDelayTest : public testing::Test,
+ public TraceEventSyntheticDelayClock {
+ public:
+ TraceEventSyntheticDelayTest() {}
+ virtual ~TraceEventSyntheticDelayTest() {
+ ResetTraceEventSyntheticDelays();
+ }
+
+ // TraceEventSyntheticDelayClock implementation.
+ virtual base::TimeTicks Now() OVERRIDE {
+ AdvanceTime(base::TimeDelta::FromMilliseconds(kShortDurationMs / 10));
+ return now_;
+ }
+
+ TraceEventSyntheticDelay* ConfigureDelay(const char* name) {
+ TraceEventSyntheticDelay* delay = TraceEventSyntheticDelay::Lookup(name);
+ delay->SetClock(this);
+ delay->SetTargetDuration(
+ base::TimeDelta::FromMilliseconds(kTargetDurationMs));
+ return delay;
+ }
+
+ void AdvanceTime(base::TimeDelta delta) { now_ += delta; }
+
+ int TestFunction() {
+ base::TimeTicks start = Now();
+ { TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); }
+ return (Now() - start).InMilliseconds();
+ }
+
+ int AsyncTestFunctionBegin() {
+ base::TimeTicks start = Now();
+ { TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("test.AsyncDelay"); }
+ return (Now() - start).InMilliseconds();
+ }
+
+ int AsyncTestFunctionEnd() {
+ base::TimeTicks start = Now();
+ { TRACE_EVENT_SYNTHETIC_DELAY_END("test.AsyncDelay"); }
+ return (Now() - start).InMilliseconds();
+ }
+
+ private:
+ base::TimeTicks now_;
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayTest);
+};
+
+TEST_F(TraceEventSyntheticDelayTest, StaticDelay) {
+ TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay");
+ delay->SetMode(TraceEventSyntheticDelay::STATIC);
+ EXPECT_GE(TestFunction(), kTargetDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, OneShotDelay) {
+ TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay");
+ delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT);
+ EXPECT_GE(TestFunction(), kTargetDurationMs);
+ EXPECT_LT(TestFunction(), kShortDurationMs);
+
+ delay->SetTargetDuration(
+ base::TimeDelta::FromMilliseconds(kTargetDurationMs));
+ EXPECT_GE(TestFunction(), kTargetDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AlternatingDelay) {
+ TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay");
+ delay->SetMode(TraceEventSyntheticDelay::ALTERNATING);
+ EXPECT_GE(TestFunction(), kTargetDurationMs);
+ EXPECT_LT(TestFunction(), kShortDurationMs);
+ EXPECT_GE(TestFunction(), kTargetDurationMs);
+ EXPECT_LT(TestFunction(), kShortDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AsyncDelay) {
+ ConfigureDelay("test.AsyncDelay");
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AsyncDelayExceeded) {
+ ConfigureDelay("test.AsyncDelay");
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ AdvanceTime(base::TimeDelta::FromMilliseconds(kTargetDurationMs));
+ EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNoActivation) {
+ ConfigureDelay("test.AsyncDelay");
+ EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNested) {
+ ConfigureDelay("test.AsyncDelay");
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs);
+ EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, AsyncDelayUnbalanced) {
+ ConfigureDelay("test.AsyncDelay");
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2);
+ EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs);
+
+ EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs);
+ EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, ResetDelays) {
+ ConfigureDelay("test.Delay");
+ ResetTraceEventSyntheticDelays();
+ EXPECT_LT(TestFunction(), kShortDurationMs);
+}
+
+TEST_F(TraceEventSyntheticDelayTest, BeginParallel) {
+ TraceEventSyntheticDelay* delay = ConfigureDelay("test.AsyncDelay");
+ base::TimeTicks end_times[2];
+ base::TimeTicks start_time = Now();
+
+ delay->BeginParallel(&end_times[0]);
+ EXPECT_FALSE(end_times[0].is_null());
+
+ delay->BeginParallel(&end_times[1]);
+ EXPECT_FALSE(end_times[1].is_null());
+
+ delay->EndParallel(end_times[0]);
+ EXPECT_GE((Now() - start_time).InMilliseconds(), kTargetDurationMs);
+
+ start_time = Now();
+ delay->EndParallel(end_times[1]);
+ EXPECT_LT((Now() - start_time).InMilliseconds(), kShortDurationMs);
+}
+
+} // namespace debug
+} // namespace base
diff --git a/chromium/base/debug/trace_event_unittest.cc b/chromium/base/debug/trace_event_unittest.cc
index aef2c43b6fa..c843c25f540 100644
--- a/chromium/base/debug/trace_event_unittest.cc
+++ b/chromium/base/debug/trace_event_unittest.cc
@@ -4,11 +4,13 @@
#include "base/debug/trace_event_unittest.h"
+#include <math.h>
#include <cstdlib>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
+#include "base/debug/trace_event_synthetic_delay.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/memory/ref_counted_memory.h"
@@ -19,6 +21,7 @@
#include "base/synchronization/waitable_event.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
+#include "base/time/time.h"
#include "base/values.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -62,6 +65,7 @@ class TraceEventTestFixture : public testing::Test {
const char* phase,
const char* key,
const char* value);
+ void DropTracedMetadataRecords();
bool FindMatchingValue(const char* key,
const char* value);
bool FindNonMatchingValue(const char* key,
@@ -78,6 +82,7 @@ class TraceEventTestFixture : public testing::Test {
void BeginSpecificTrace(const std::string& filter) {
event_watch_notification_ = 0;
TraceLog::GetInstance()->SetEnabled(CategoryFilter(filter),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
}
@@ -247,6 +252,28 @@ DictionaryValue* TraceEventTestFixture::FindMatchingTraceEntry(
return NULL;
}
+void TraceEventTestFixture::DropTracedMetadataRecords() {
+
+ scoped_ptr<ListValue> old_trace_parsed(trace_parsed_.DeepCopy());
+ size_t old_trace_parsed_size = old_trace_parsed->GetSize();
+ trace_parsed_.Clear();
+
+ for (size_t i = 0; i < old_trace_parsed_size; i++) {
+ Value* value = NULL;
+ old_trace_parsed->Get(i, &value);
+ if (!value || value->GetType() != Value::TYPE_DICTIONARY) {
+ trace_parsed_.Append(value->DeepCopy());
+ continue;
+ }
+ DictionaryValue* dict = static_cast<DictionaryValue*>(value);
+ std::string tmp;
+ if(dict->GetString("ph", &tmp) && tmp == "M")
+ continue;
+
+ trace_parsed_.Append(value->DeepCopy());
+ }
+}
+
DictionaryValue* TraceEventTestFixture::FindNamePhase(const char* name,
const char* phase) {
JsonKeyValue key_values[] = {
@@ -853,6 +880,7 @@ void HighResSleepForTraceTest(base::TimeDelta elapsed) {
// Simple Test for emitting data and validating it was received.
TEST_F(TraceEventTestFixture, DataCaptured) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TraceWithAllMacroVariants(NULL);
@@ -876,6 +904,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) {
EXPECT_CALL(observer, OnTraceLogEnabled())
.Times(1);
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
testing::Mock::VerifyAndClear(&observer);
EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
@@ -887,6 +916,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnEnable) {
TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
testing::StrictMock<MockEnabledStateChangedObserver> observer;
@@ -897,6 +927,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
EXPECT_CALL(observer, OnTraceLogDisabled())
.Times(0);
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
testing::Mock::VerifyAndClear(&observer);
EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
@@ -909,8 +940,12 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) {
CategoryFilter cf_inc_all("*");
- TraceLog::GetInstance()->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL);
- TraceLog::GetInstance()->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(cf_inc_all,
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(cf_inc_all,
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
testing::StrictMock<MockEnabledStateChangedObserver> observer;
TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
@@ -929,6 +964,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverFiresOnFirstDisable) {
TEST_F(TraceEventTestFixture, EnabledObserverFiresOnDisable) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
MockEnabledStateChangedObserver observer;
@@ -965,6 +1001,7 @@ TEST_F(TraceEventTestFixture, ObserversFireAfterStateChange) {
TraceLog::GetInstance()->AddEnabledStateObserver(&observer);
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(TraceLog::GetInstance()->IsEnabled());
@@ -997,6 +1034,7 @@ TEST_F(TraceEventTestFixture, SelfRemovingObserver) {
EXPECT_EQ(1u, TraceLog::GetInstance()->GetObserverCountForTest());
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TraceLog::GetInstance()->SetDisabled();
// The observer removed itself on disable.
@@ -1012,6 +1050,7 @@ bool IsNewTrace() {
TEST_F(TraceEventTestFixture, NewTraceRecording) {
ASSERT_FALSE(IsNewTrace());
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
// First call to IsNewTrace() should succeed. But, the second shouldn't.
ASSERT_TRUE(IsNewTrace());
@@ -1024,6 +1063,7 @@ TEST_F(TraceEventTestFixture, NewTraceRecording) {
// Start another trace. IsNewTrace() should become true again, briefly, as
// before.
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
ASSERT_TRUE(IsNewTrace());
ASSERT_FALSE(IsNewTrace());
@@ -1080,20 +1120,24 @@ TEST_F(TraceEventTestFixture, Categories) {
Clear();
included_categories.clear();
TraceLog::GetInstance()->SetEnabled(CategoryFilter("not_found823564786"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD);
TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
+ DropTracedMetadataRecords();
EXPECT_TRUE(trace_parsed_.empty());
// Include existent category -> only events of that category
Clear();
included_categories.clear();
TraceLog::GetInstance()->SetEnabled(CategoryFilter("inc"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD);
TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
+ DropTracedMetadataRecords();
EXPECT_TRUE(FindMatchingValue("cat", "inc"));
EXPECT_FALSE(FindNonMatchingValue("cat", "inc"));
@@ -1102,6 +1146,7 @@ TEST_F(TraceEventTestFixture, Categories) {
included_categories.clear();
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("inc_wildcard_*,inc_wildchar_?_end"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("inc_wildcard_abc", "included",
TRACE_EVENT_SCOPE_THREAD);
@@ -1131,6 +1176,7 @@ TEST_F(TraceEventTestFixture, Categories) {
// Exclude nonexistent category -> all events
Clear();
TraceLog::GetInstance()->SetEnabled(CategoryFilter("-not_found823564786"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("cat1", "name", TRACE_EVENT_SCOPE_THREAD);
TRACE_EVENT_INSTANT0("cat2", "name", TRACE_EVENT_SCOPE_THREAD);
@@ -1143,6 +1189,7 @@ TEST_F(TraceEventTestFixture, Categories) {
// Exclude existent category -> only events of other categories
Clear();
TraceLog::GetInstance()->SetEnabled(CategoryFilter("-inc"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("inc", "name", TRACE_EVENT_SCOPE_THREAD);
TRACE_EVENT_INSTANT0("inc2", "name", TRACE_EVENT_SCOPE_THREAD);
@@ -1158,6 +1205,7 @@ TEST_F(TraceEventTestFixture, Categories) {
Clear();
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("-inc_wildcard_*,-inc_wildchar_?_end"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("inc_wildcard_abc", "not_inc",
TRACE_EVENT_SCOPE_THREAD);
@@ -1390,7 +1438,7 @@ TEST_F(TraceEventTestFixture, DataCapturedManyThreads) {
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());
+ threads[i] = new Thread(StringPrintf("Thread %d", i));
task_complete_events[i] = new WaitableEvent(false, false);
threads[i]->Start();
threads[i]->message_loop()->PostTask(
@@ -1430,7 +1478,7 @@ TEST_F(TraceEventTestFixture, ThreadNames) {
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());
+ threads[i] = new Thread(StringPrintf("Thread %d", i));
// Enable tracing.
BeginTrace();
@@ -1665,15 +1713,21 @@ TEST_F(TraceEventTestFixture, TracingIsLazy) {
TEST_F(TraceEventTestFixture, TraceEnableDisable) {
TraceLog* trace_log = TraceLog::GetInstance();
CategoryFilter cf_inc_all("*");
- trace_log->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(cf_inc_all,
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(trace_log->IsEnabled());
trace_log->SetDisabled();
EXPECT_FALSE(trace_log->IsEnabled());
- trace_log->SetEnabled(cf_inc_all, TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(cf_inc_all,
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(trace_log->IsEnabled());
const std::vector<std::string> empty;
- trace_log->SetEnabled(CategoryFilter(""), TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(CategoryFilter(""),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(trace_log->IsEnabled());
trace_log->SetDisabled();
EXPECT_FALSE(trace_log->IsEnabled());
@@ -1683,15 +1737,21 @@ TEST_F(TraceEventTestFixture, TraceEnableDisable) {
TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
TraceLog* trace_log = TraceLog::GetInstance();
- trace_log->SetEnabled(CategoryFilter("foo,bar"), TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(CategoryFilter("foo,bar"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("bar"));
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
- trace_log->SetEnabled(CategoryFilter("foo2"), TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(CategoryFilter("foo2"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo2"));
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
// The "" becomes the default catergory set when applied.
- trace_log->SetEnabled(CategoryFilter(""), TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(CategoryFilter(""),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("foo"));
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
EXPECT_STREQ("-*Debug,-*Test",
@@ -1703,10 +1763,13 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("baz"));
trace_log->SetEnabled(CategoryFilter("-foo,-bar"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo"));
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
- trace_log->SetEnabled(CategoryFilter("moo"), TraceLog::RECORD_UNTIL_FULL);
+ trace_log->SetEnabled(CategoryFilter("moo"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("baz"));
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("moo"));
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("foo"));
@@ -1717,9 +1780,11 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
// Make sure disabled categories aren't cleared if we set in the second.
trace_log->SetEnabled(CategoryFilter("disabled-by-default-cc,foo"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
EXPECT_FALSE(*trace_log->GetCategoryGroupEnabled("bar"));
trace_log->SetEnabled(CategoryFilter("disabled-by-default-gpu"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-cc"));
EXPECT_TRUE(*trace_log->GetCategoryGroupEnabled("disabled-by-default-gpu"));
@@ -1733,6 +1798,7 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
TEST_F(TraceEventTestFixture, TraceSampling) {
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::Options(TraceLog::RECORD_UNTIL_FULL |
TraceLog::ENABLE_SAMPLING));
@@ -1751,6 +1817,7 @@ TEST_F(TraceEventTestFixture, TraceSampling) {
TEST_F(TraceEventTestFixture, TraceSamplingScope) {
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::Options(TraceLog::RECORD_UNTIL_FULL |
TraceLog::ENABLE_SAMPLING));
@@ -1785,7 +1852,8 @@ TEST_F(TraceEventTestFixture, TraceSamplingScope) {
TEST_F(TraceEventTestFixture, TraceContinuousSampling) {
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("*"),
- TraceLog::Options(TraceLog::MONITOR_SAMPLING));
+ base::debug::TraceLog::MONITORING_MODE,
+ TraceLog::Options(TraceLog::ENABLE_SAMPLING));
TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "AAA");
TraceLog::GetInstance()->WaitSamplingEventForTesting();
@@ -1843,6 +1911,7 @@ class MyData : public base::debug::ConvertableToTraceFormat {
TEST_F(TraceEventTestFixture, ConvertableTypes) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
scoped_refptr<ConvertableToTraceFormat> data(new MyData());
@@ -1946,6 +2015,171 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) {
EXPECT_EQ(1, foo_val);
}
+TEST_F(TraceEventTestFixture, PrimitiveArgs) {
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
+
+ TRACE_EVENT1("foo", "event1", "int_one", 1);
+ TRACE_EVENT1("foo", "event2", "int_neg_ten", -10);
+ TRACE_EVENT1("foo", "event3", "float_one", 1.0f);
+ TRACE_EVENT1("foo", "event4", "float_half", .5f);
+ TRACE_EVENT1("foo", "event5", "float_neghalf", -.5f);
+ TRACE_EVENT1("foo", "event6", "float_infinity",
+ std::numeric_limits<float>::infinity());
+ TRACE_EVENT1("foo", "event6b", "float_neg_infinity",
+ -std::numeric_limits<float>::infinity());
+ TRACE_EVENT1("foo", "event7", "double_nan",
+ std::numeric_limits<double>::quiet_NaN());
+ void* p = 0;
+ TRACE_EVENT1("foo", "event8", "pointer_null", p);
+ p = reinterpret_cast<void*>(0xbadf00d);
+ TRACE_EVENT1("foo", "event9", "pointer_badf00d", p);
+ TRACE_EVENT1("foo", "event10", "bool_true", true);
+ TRACE_EVENT1("foo", "event11", "bool_false", false);
+ TRACE_EVENT1("foo", "event12", "time_null",
+ base::Time());
+ TRACE_EVENT1("foo", "event13", "time_one",
+ base::Time::FromInternalValue(1));
+ TRACE_EVENT1("foo", "event14", "timeticks_null",
+ base::TimeTicks());
+ TRACE_EVENT1("foo", "event15", "timeticks_one",
+ base::TimeTicks::FromInternalValue(1));
+ EndTraceAndFlush();
+
+ const DictionaryValue* args_dict = NULL;
+ DictionaryValue* dict = NULL;
+ const Value* value = NULL;
+ std::string str_value;
+ int int_value;
+ double double_value;
+ bool bool_value;
+
+ dict = FindNamePhase("event1", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("int_one", &int_value));
+ EXPECT_EQ(1, int_value);
+
+ dict = FindNamePhase("event2", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("int_neg_ten", &int_value));
+ EXPECT_EQ(-10, int_value);
+
+ // 1f must be serlized to JSON as "1.0" in order to be a double, not an int.
+ dict = FindNamePhase("event3", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->Get("float_one", &value));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+ EXPECT_TRUE(value->GetAsDouble(&double_value));
+ EXPECT_EQ(1, double_value);
+
+ // .5f must be serlized to JSON as "0.5".
+ dict = FindNamePhase("event4", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->Get("float_half", &value));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+ EXPECT_TRUE(value->GetAsDouble(&double_value));
+ EXPECT_EQ(0.5, double_value);
+
+ // -.5f must be serlized to JSON as "-0.5".
+ dict = FindNamePhase("event5", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->Get("float_neghalf", &value));
+ EXPECT_TRUE(value->IsType(Value::TYPE_DOUBLE));
+ EXPECT_TRUE(value->GetAsDouble(&double_value));
+ EXPECT_EQ(-0.5, double_value);
+
+ // Infinity is serialized to JSON as a string.
+ dict = FindNamePhase("event6", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetString("float_infinity", &str_value));
+ EXPECT_STREQ("Infinity", str_value.c_str());
+ dict = FindNamePhase("event6b", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetString("float_neg_infinity", &str_value));
+ EXPECT_STREQ("-Infinity", str_value.c_str());
+
+ // NaN is serialized to JSON as a string.
+ dict = FindNamePhase("event7", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetString("double_nan", &str_value));
+ EXPECT_STREQ("NaN", str_value.c_str());
+
+ // NULL pointers should be serialized as "0x0".
+ dict = FindNamePhase("event8", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetString("pointer_null", &str_value));
+ EXPECT_STREQ("0x0", str_value.c_str());
+
+ // Other pointers should be serlized as a hex string.
+ dict = FindNamePhase("event9", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetString("pointer_badf00d", &str_value));
+ EXPECT_STREQ("0xbadf00d", str_value.c_str());
+
+ dict = FindNamePhase("event10", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetBoolean("bool_true", &bool_value));
+ EXPECT_TRUE(bool_value);
+
+ dict = FindNamePhase("event11", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetBoolean("bool_false", &bool_value));
+ EXPECT_FALSE(bool_value);
+
+ dict = FindNamePhase("event12", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("time_null", &int_value));
+ EXPECT_EQ(0, int_value);
+
+ dict = FindNamePhase("event13", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("time_one", &int_value));
+ EXPECT_EQ(1, int_value);
+
+ dict = FindNamePhase("event14", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("timeticks_null", &int_value));
+ EXPECT_EQ(0, int_value);
+
+ dict = FindNamePhase("event15", "X");
+ ASSERT_TRUE(dict);
+ dict->GetDictionary("args", &args_dict);
+ ASSERT_TRUE(args_dict);
+ EXPECT_TRUE(args_dict->GetInteger("timeticks_one", &int_value));
+ EXPECT_EQ(1, int_value);
+}
+
class TraceEventCallbackTest : public TraceEventTestFixture {
public:
virtual void SetUp() OVERRIDE {
@@ -2046,6 +2280,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallback) {
TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_UNTIL_FULL);
do {
TRACE_EVENT_INSTANT0("all", "badger badger", TRACE_EVENT_SCOPE_GLOBAL);
@@ -2066,8 +2301,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) {
Callback);
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEnabled(
- CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
TraceLog::GetInstance()->SetEventCallbackDisabled();
@@ -2077,6 +2313,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ DropTracedMetadataRecords();
VerifyCallbackAndRecordedEvents(2, 2);
}
@@ -2088,8 +2325,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) {
Callback);
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEnabled(
- CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
EndTraceAndFlush();
@@ -2099,6 +2337,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ DropTracedMetadataRecords();
VerifyCallbackAndRecordedEvents(3, 1);
}
@@ -2106,8 +2345,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) {
TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEnabled(
- CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
@@ -2121,6 +2361,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ DropTracedMetadataRecords();
VerifyCallbackAndRecordedEvents(1, 3);
}
@@ -2128,8 +2369,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) {
TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEnabled(
- CategoryFilter("recording"), TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("recording"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
@@ -2143,6 +2385,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) {
TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ DropTracedMetadataRecords();
VerifyCallbackAndRecordedEvents(2, 2);
}
@@ -2151,8 +2394,9 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) {
Callback);
{
TRACE_EVENT0("callback", "duration1");
- TraceLog::GetInstance()->SetEnabled(
- CategoryFilter("*"), TraceLog::RECORD_UNTIL_FULL);
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
+ TraceLog::RECORD_UNTIL_FULL);
TRACE_EVENT0("callback", "duration2");
EndTraceAndFlush();
TRACE_EVENT0("callback", "duration3");
@@ -2170,6 +2414,7 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) {
TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_CONTINUOUSLY);
TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
size_t capacity = buffer->Capacity();
@@ -2229,6 +2474,7 @@ TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) {
TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_CONTINUOUSLY);
TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
size_t capacity = buffer->Capacity();
@@ -2256,6 +2502,7 @@ TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) {
TEST_F(TraceEventTestFixture, TraceBufferRingBufferFullIteration) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::RECORD_CONTINUOUSLY);
TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
size_t capacity = buffer->Capacity();
@@ -2504,6 +2751,7 @@ TEST_F(TraceEventTestFixture, EchoToConsole) {
logging::SetLogMessageHandler(MockLogMessageHandler);
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::ECHO_TO_CONSOLE);
TRACE_EVENT_BEGIN0("a", "begin_end");
{
@@ -2539,6 +2787,7 @@ TEST_F(TraceEventTestFixture, EchoToConsoleTraceEventRecursion) {
logging::SetLogMessageHandler(LogMessageHandlerWithTraceEvent);
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ base::debug::TraceLog::RECORDING_MODE,
TraceLog::ECHO_TO_CONSOLE);
{
// This should not cause deadlock or infinite recursion.
@@ -2567,6 +2816,7 @@ TEST_F(TraceEventTestFixture, TimeOffset) {
TimeTicks::NowFromSystemTraceTime().ToInternalValue());
EndTraceAndFlush();
+ DropTracedMetadataRecords();
double end_time = static_cast<double>(
(TimeTicks::NowFromSystemTraceTime() - time_offset).ToInternalValue());
@@ -2582,5 +2832,48 @@ TEST_F(TraceEventTestFixture, TimeOffset) {
}
}
+TEST_F(TraceEventTestFixture, ConfigureSyntheticDelays) {
+ BeginSpecificTrace("DELAY(test.Delay;0.05)");
+
+ base::TimeTicks start = base::TimeTicks::Now();
+ {
+ TRACE_EVENT_SYNTHETIC_DELAY("test.Delay");
+ }
+ base::TimeDelta duration = base::TimeTicks::Now() - start;
+ EXPECT_GE(duration.InMilliseconds(), 50);
+
+ EndTraceAndFlush();
+}
+
+TEST_F(TraceEventTestFixture, BadSyntheticDelayConfigurations) {
+ const char* configs[] = {
+ "",
+ "DELAY(",
+ "DELAY(;",
+ "DELAY(;)",
+ "DELAY(test.Delay)",
+ "DELAY(test.Delay;)"
+ };
+ for (size_t i = 0; i < arraysize(configs); i++) {
+ BeginSpecificTrace(configs[i]);
+ EndTraceAndFlush();
+ CategoryFilter filter = TraceLog::GetInstance()->GetCurrentCategoryFilter();
+ EXPECT_EQ(0u, filter.GetSyntheticDelayValues().size());
+ }
+}
+
+TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationMerging) {
+ CategoryFilter filter1("DELAY(test.Delay1;16)");
+ CategoryFilter filter2("DELAY(test.Delay2;32)");
+ filter1.Merge(filter2);
+ EXPECT_EQ(2u, filter1.GetSyntheticDelayValues().size());
+}
+
+TEST_F(TraceEventTestFixture, SyntheticDelayConfigurationToString) {
+ const char config[] = "DELAY(test.Delay;16;oneshot)";
+ CategoryFilter filter(config);
+ EXPECT_EQ(config, filter.ToString());
+}
+
} // namespace debug
} // namespace base
diff --git a/chromium/base/debug/tsan_suppressions.cc b/chromium/base/debug/tsan_suppressions.cc
new file mode 100644
index 00000000000..70ce21ee563
--- /dev/null
+++ b/chromium/base/debug/tsan_suppressions.cc
@@ -0,0 +1,297 @@
+// Copyright 2014 The Chromium 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 default suppressions for ThreadSanitizer.
+// You can also pass additional suppressions via TSAN_OPTIONS:
+// TSAN_OPTIONS=suppressions=/path/to/suppressions. Please refer to
+// http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
+// for more info.
+
+#if defined(THREAD_SANITIZER)
+
+// Please make sure the code below declares a single string variable
+// kTSanDefaultSuppressions contains TSan suppressions delimited by newlines.
+// See http://dev.chromium.org/developers/testing/threadsanitizer-tsan-v2
+// for the instructions on writing suppressions.
+char kTSanDefaultSuppressions[] =
+// False positives in libflashplayer.so and libglib.so. Since we don't
+// instrument them, we cannot reason about the synchronization in them.
+"race:libflashplayer.so\n"
+"race:libglib*.so\n"
+
+// Intentional race in ToolsSanityTest.DataRace in base_unittests.
+"race:base/tools_sanity_unittest.cc\n"
+
+// Data race on WatchdogCounter [test-only].
+"race:base/threading/watchdog_unittest.cc\n"
+
+// Races in libevent, http://crbug.com/23244.
+"race:libevent/event.c\n"
+
+// http://crbug.com/46840.
+"race:base::HistogramSamples::IncreaseSum\n"
+"race:base::Histogram::Add\n"
+"race:base::HistogramSamples::Add\n"
+
+// http://crbug.com/84094.
+"race:sqlite3StatusSet\n"
+"race:pcache1EnforceMaxPage\n"
+"race:pcache1AllocPage\n"
+
+// http://crbug.com/102327.
+// Test-only race, won't fix.
+"race:tracked_objects::ThreadData::ShutdownSingleThreadedCleanup\n"
+
+// http://crbug.com/115540
+"race:*GetCurrentThreadIdentifier\n"
+
+// http://crbug.com/120808
+"race:base/threading/watchdog.cc\n"
+
+// http://crbug.com/157586
+"race:third_party/libvpx/source/libvpx/vp8/decoder/threading.c\n"
+
+// http://crbug.com/158718
+"race:third_party/ffmpeg/libavcodec/pthread.c\n"
+"race:third_party/ffmpeg/libavcodec/pthread_frame.c\n"
+"race:third_party/ffmpeg/libavcodec/vp8.c\n"
+"race:third_party/ffmpeg/libavutil/mem.c\n"
+"race:*HashFrameForTesting\n"
+"race:third_party/ffmpeg/libavcodec/h264pred.c\n"
+"race:media::ReleaseData\n"
+
+// http://crbug.com/158922
+"race:third_party/libvpx/source/libvpx/vp8/encoder/*\n"
+
+// http://crbug.com/189177
+"race:thread_manager\n"
+"race:v8::Locker::Initialize\n"
+
+// http://crbug.com/223352
+"race:uprv_malloc_46\n"
+"race:uprv_realloc_46\n"
+
+// http://crbug.com/239359
+"race:media::TestInputCallback::OnData\n"
+
+// http://crbug.com/244368
+"race:skia::BeginPlatformPaint\n"
+
+// http://crbug.com/244385
+"race:unixTempFileDir\n"
+
+// http://crbug.com/244755
+"race:v8::internal::Zone::NewExpand\n"
+"race:TooLateToEnableNow\n"
+"race:adjust_segment_bytes_allocated\n"
+
+// http://crbug.com/244774
+"race:webrtc::RTPReceiver::ProcessBitrate\n"
+"race:webrtc::RTPSender::ProcessBitrate\n"
+"race:webrtc::VideoCodingModuleImpl::Decode\n"
+"race:webrtc::RTPSender::SendOutgoingData\n"
+"race:webrtc::VP8EncoderImpl::GetEncodedPartitions\n"
+"race:webrtc::VP8EncoderImpl::Encode\n"
+"race:webrtc::ViEEncoder::DeliverFrame\n"
+"race:webrtc::vcm::VideoReceiver::Decode\n"
+"race:webrtc::VCMReceiver::FrameForDecoding\n"
+"race:*trace_event_unique_catstatic*\n"
+
+// http://crbug.com/244856
+"race:AutoPulseLock\n"
+
+// http://crbug.com/246968
+"race:webrtc::VideoCodingModuleImpl::RegisterPacketRequestCallback\n"
+
+// http://crbug.com/246970
+"race:webrtc::EventPosix::StartTimer\n"
+
+// http://crbug.com/246974
+"race:content::GpuWatchdogThread::CheckArmed\n"
+
+// http://crbug.com/257396
+"race:base::debug::TraceEventTestFixture_TraceSamplingScope_Test::TestBody\n"
+
+// http://crbug.com/258479
+"race:SamplingStateScope\n"
+"race:g_trace_state\n"
+
+// http://crbug.com/258499
+"race:third_party/skia/include/core/SkRefCnt.h\n"
+
+// http://crbug.com/268924
+"race:base::g_power_monitor\n"
+"race:base::PowerMonitor::PowerMonitor\n"
+"race:base::PowerMonitor::AddObserver\n"
+
+// http://crbug.com/268941
+"race:tracked_objects::ThreadData::tls_index_\n"
+
+// http://crbug.com/270037
+"race:gLibCleanupFunctions\n"
+
+// http://crbug.com/272095
+"race:base::g_top_manager\n"
+
+// http://crbug.com/272987
+"race:webrtc::MediaStreamTrack<webrtc::AudioTrackInterface>::set_enabled\n"
+
+// http://crbug.com/273047
+"race:base::*::g_lazy_tls_ptr\n"
+"race:IPC::SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_\n"
+
+// http://crbug.com/280466
+"race:content::WebRtcAudioCapturer::SetCapturerSource\n"
+
+// http://crbug.com/285242
+"race:media::PulseAudioOutputStream::SetVolume\n"
+
+// http://crbug.com/290964
+"race:PostponeInterruptsScope\n"
+"race:v8::internal::StackGuard::RequestInstallCode\n"
+
+// http://crbug.com/296883
+"race:net::URLFetcherCore::Stop\n"
+
+// http://crbug.com/308590
+"race:CustomThreadWatcher::~CustomThreadWatcher\n"
+
+// http://crbug.com/310851
+"race:net::ProxyResolverV8Tracing::Job::~Job\n"
+
+// http://crbug.com/313726
+"race:CallbackWasCalled\n"
+
+// http://crbug.com/327330
+"race:PrepareTextureMailbox\n"
+"race:cc::LayerTreeHost::PaintLayerContents\n"
+
+// http://crbug.com/328804
+"race:v8::internal::Heap::SetStackLimits\n"
+"race:ScavengePointer\n"
+
+// http://crbug.com/328826
+"race:gLCDOrder\n"
+"race:gLCDOrientation\n"
+
+// http://crbug.com/328868
+"race:PR_Lock\n"
+
+// http://crbug.com/329225
+"race:blink::currentTimeFunction\n"
+
+// http://crbug.com/329460
+"race:extensions::InfoMap::AddExtension\n"
+
+// http://crbug.com/330528
+"race:v8::internal::MarkCompactCollector::SweepInParallel\n"
+
+// http://crbug.com/333244
+"race:content::"
+ "VideoCaptureImplTest::MockVideoCaptureImpl::~MockVideoCaptureImpl\n"
+
+// http://crbug.com/333871
+"race:v8::internal::Interface::NewValue()::value_interface\n"
+"race:v8::internal::IsMinusZero(double)::minus_zero\n"
+"race:v8::internal::FastCloneShallowObjectStub::InitializeInterfaceDescriptor\n"
+"race:v8::internal::KeyedLoadStubCompiler::registers\n"
+"race:v8::internal::KeyedStoreStubCompiler::registers()::registers\n"
+"race:v8::internal::KeyedLoadFastElementStub::InitializeInterfaceDescriptor\n"
+"race:v8::internal::KeyedStoreFastElementStub::InitializeInterfaceDescriptor\n"
+"race:v8::internal::LoadStubCompiler::registers\n"
+"race:v8::internal::StoreStubCompiler::registers\n"
+"race:v8::internal::HValue::LoopWeight\n"
+
+// http://crbug.com/334140
+"race:CommandLine::HasSwitch\n"
+"race:CommandLine::current_process_commandline_\n"
+"race:CommandLine::GetSwitchValueASCII\n"
+
+// http://crbug.com/338675
+"race:blink::s_platform\n"
+"race:content::"
+ "RendererWebKitPlatformSupportImpl::~RendererWebKitPlatformSupportImpl\n"
+
+// http://crbug.com/345240
+"race:WTF::s_shutdown\n"
+
+// http://crbug.com/345245
+"race:jingle_glue::JingleThreadWrapper::~JingleThreadWrapper\n"
+"race:webrtc::voe::Channel::UpdatePacketDelay\n"
+"race:webrtc::voe::Channel::GetDelayEstimate\n"
+"race:webrtc::VCMCodecDataBase::DeregisterReceiveCodec\n"
+"race:webrtc::GainControlImpl::set_stream_analog_level\n"
+
+// http://crbug.com/345618
+"race:WebCore::AudioDestinationNode::render\n"
+
+// http://crbug.com/345624
+"race:media::DataSource::set_host\n"
+
+// http://crbug.com/347534
+"race:v8::internal::V8::TearDown\n"
+
+// http://crbug.com/347538
+"race:sctp_timer_start\n"
+
+// http://crbug.com/347548
+"race:cricket::WebRtcVideoMediaChannel::MaybeResetVieSendCodec\n"
+"race:cricket::WebRtcVideoMediaChannel::SetSendCodec\n"
+
+// http://crbug.com/347553
+"race:blink::WebString::reset\n"
+
+// http://crbug.com/348511
+"race:webrtc::acm1::AudioCodingModuleImpl::PlayoutData10Ms\n"
+
+// http://crbug.com/348982
+"race:cricket::P2PTransportChannel::OnConnectionDestroyed\n"
+"race:cricket::P2PTransportChannel::AddConnection\n"
+
+// http://crbug.com/348984
+"race:sctp_express_handle_sack\n"
+
+// http://crbug.com/350982
+"race:libvpx/vp9/decoder/vp9_thread.c\n"
+
+// http://crbug.com/363999
+"race:v8::internal::EnterDebugger::*EnterDebugger\n"
+
+// http://crbug.com/364006
+"race:gfx::ImageFamily::~ImageFamily\n"
+
+// http://crbug.com/364014
+"race:WTF::Latin1Encoding()::globalLatin1Encoding\n"
+
+// https://code.google.com/p/v8/issues/detail?id=3143
+"race:v8::internal::FLAG_track_double_fields\n"
+
+// https://crbug.com/369257
+// TODO(mtklein): annotate properly and remove suppressions.
+"race:SandboxIPCHandler::HandleFontMatchRequest\n"
+"race:SkFontConfigInterfaceDirect::matchFamilyName\n"
+"race:SkFontConfigInterface::GetSingletonDirectInterface\n"
+"race:FcStrStaticName\n"
+
+// http://crbug.com/372807
+"deadlock:net::X509Certificate::CreateCertificateListFromBytes\n"
+"deadlock:net::X509Certificate::CreateFromBytes\n"
+"deadlock:net::SSLClientSocketNSS::Core::DoHandshakeLoop\n"
+
+// http://crbug.com/374135
+"race:media::AlsaWrapper::PcmWritei\n"
+
+// False positive in libc's tzset_internal, http://crbug.com/379738.
+"race:tzset_internal\n"
+
+// http://crbug.com/380554
+"deadlock:g_type_add_interface_static\n"
+
+// http:://crbug.com/386385
+"race:appcache::AppCacheStorageImpl::DatabaseTask::CallRunCompleted\n"
+
+// End of suppressions.
+; // Please keep this semicolon.
+
+#endif // THREAD_SANITIZER
diff --git a/chromium/base/file_descriptor_posix.h b/chromium/base/file_descriptor_posix.h
index abc07893faf..c730be65f74 100644
--- a/chromium/base/file_descriptor_posix.h
+++ b/chromium/base/file_descriptor_posix.h
@@ -5,6 +5,8 @@
#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_
#define BASE_FILE_DESCRIPTOR_POSIX_H_
+#include "base/files/file.h"
+
namespace base {
// -----------------------------------------------------------------------------
@@ -16,18 +18,21 @@ namespace base {
// above the template specialisation for this structure.
// -----------------------------------------------------------------------------
struct FileDescriptor {
- FileDescriptor()
- : fd(-1),
- auto_close(false) { }
+ FileDescriptor() : fd(-1), auto_close(false) {}
+
+ FileDescriptor(int ifd, bool iauto_close) : fd(ifd), auto_close(iauto_close) {
+ }
- FileDescriptor(int ifd, bool iauto_close)
- : fd(ifd),
- auto_close(iauto_close) { }
+ FileDescriptor(File file) : fd(file.TakePlatformFile()), auto_close(true) {}
bool operator==(const FileDescriptor& other) const {
return (fd == other.fd && auto_close == other.auto_close);
}
+ bool operator!=(const FileDescriptor& other) const {
+ return !operator==(other);
+ }
+
// 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;
diff --git a/chromium/base/file_util.cc b/chromium/base/file_util.cc
index 1575a079d6b..d11cd15a2e0 100644
--- a/chromium/base/file_util.cc
+++ b/chromium/base/file_util.cc
@@ -10,6 +10,7 @@
#include <stdio.h>
#include <fstream>
+#include <limits>
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
@@ -126,7 +127,11 @@ bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
return true;
}
-bool ReadFileToString(const FilePath& path, std::string* contents) {
+bool ReadFileToString(const FilePath& path,
+ std::string* contents,
+ size_t max_size) {
+ if (contents)
+ contents->clear();
if (path.ReferencesParent())
return false;
FILE* file = OpenFile(path, "rb");
@@ -136,13 +141,30 @@ bool ReadFileToString(const FilePath& path, std::string* contents) {
char buf[1 << 16];
size_t len;
+ size_t size = 0;
+ bool read_status = true;
+
+ // Many files supplied in |path| have incorrect size (proc files etc).
+ // Hence, the file is read sequentially as opposed to a one-shot read.
while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
if (contents)
- contents->append(buf, len);
+ contents->append(buf, std::min(len, max_size - size));
+
+ if ((max_size - size) < len) {
+ read_status = false;
+ break;
+ }
+
+ size += len;
}
+ read_status = read_status && !ferror(file);
CloseFile(file);
- return true;
+ return read_status;
+}
+
+bool ReadFileToString(const FilePath& path, std::string* contents) {
+ return ReadFileToString(path, contents, std::numeric_limits<size_t>::max());
}
bool IsDirectoryEmpty(const FilePath& dir_path) {
@@ -166,7 +188,7 @@ bool CreateDirectory(const FilePath& full_path) {
}
bool GetFileSize(const FilePath& file_path, int64* file_size) {
- PlatformFileInfo info;
+ File::Info info;
if (!GetFileInfo(file_path, &info))
return false;
*file_size = info.size;
@@ -176,22 +198,19 @@ bool GetFileSize(const FilePath& file_path, int64* file_size) {
bool TouchFile(const FilePath& path,
const Time& last_accessed,
const Time& last_modified) {
- int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE_ATTRIBUTES;
+ int flags = File::FLAG_OPEN | File::FLAG_WRITE_ATTRIBUTES;
#if defined(OS_WIN)
// On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
if (DirectoryExists(path))
- flags |= PLATFORM_FILE_BACKUP_SEMANTICS;
+ flags |= File::FLAG_BACKUP_SEMANTICS;
#endif // OS_WIN
- const PlatformFile file = CreatePlatformFile(path, flags, NULL, NULL);
- if (file != kInvalidPlatformFileValue) {
- bool result = TouchPlatformFile(file, last_accessed, last_modified);
- ClosePlatformFile(file);
- return result;
- }
+ File file(path, flags);
+ if (!file.IsValid())
+ return false;
- return false;
+ return file.SetTimes(last_accessed, last_modified);
}
bool CloseFile(FILE* file) {
@@ -218,18 +237,8 @@ bool TruncateFile(FILE* file) {
return true;
}
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::FilePath;
-using base::kMaxUniqueFiles;
-
-int GetUniquePathNumber(
- const FilePath& path,
- const FilePath::StringType& suffix) {
+int GetUniquePathNumber(const FilePath& path,
+ const FilePath::StringType& suffix) {
bool have_suffix = !suffix.empty();
if (!PathExists(path) &&
(!have_suffix || !PathExists(FilePath(path.value() + suffix)))) {
@@ -238,8 +247,7 @@ int GetUniquePathNumber(
FilePath new_path;
for (int count = 1; count <= kMaxUniqueFiles; ++count) {
- new_path =
- path.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", count));
+ new_path = path.InsertBeforeExtensionASCII(StringPrintf(" (%d)", count));
if (!PathExists(new_path) &&
(!have_suffix || !PathExists(FilePath(new_path.value() + suffix)))) {
return count;
@@ -249,4 +257,4 @@ int GetUniquePathNumber(
return -1;
}
-} // namespace file_util
+} // namespace base
diff --git a/chromium/base/file_util.h b/chromium/base/file_util.h
index 3f892e3ad14..bb04e629771 100644
--- a/chromium/base/file_util.h
+++ b/chromium/base/file_util.h
@@ -25,9 +25,9 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
-#include "base/platform_file.h"
#include "base/strings/string16.h"
#if defined(OS_POSIX)
@@ -93,10 +93,13 @@ BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path);
// Returns false on failure and sets *error appropriately, if it is non-NULL.
BASE_EXPORT bool ReplaceFile(const FilePath& from_path,
const FilePath& to_path,
- PlatformFileError* error);
+ File::Error* error);
// Copies a single file. Use CopyDirectory to copy directories.
// This function fails if either path contains traversal components ('..').
+//
+// This function keeps the metadata on Windows. The read only bit on Windows is
+// not kept.
BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path);
// Copies the given path, and optionally all subdirectories and their contents
@@ -105,6 +108,9 @@ BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path);
// If there are files existing under to_path, always overwrite. Returns true
// if successful, false otherwise. Wildcards on the names are not supported.
//
+// This function calls into CopyFile() so the same behavior w.r.t. metadata
+// applies.
+//
// 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,
@@ -130,13 +136,28 @@ BASE_EXPORT bool ContentsEqual(const FilePath& filename1,
BASE_EXPORT bool TextContentsEqual(const FilePath& filename1,
const FilePath& filename2);
-// 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.
+// Reads the file at |path| into |contents| and returns true on success and
+// false on error. For security reasons, a |path| containing path traversal
+// components ('..') is treated as a read error and |contents| is set to empty.
+// In case of I/O error, |contents| holds the data that could be read from the
+// file before the error occurred.
+// |contents| may be NULL, in which case this function is useful for its side
+// effect of priming the disk cache (could be used for unit tests).
BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents);
+// Reads the file at |path| into |contents| and returns true on success and
+// false on error. For security reasons, a |path| containing path traversal
+// components ('..') is treated as a read error and |contents| is set to empty.
+// In case of I/O error, |contents| holds the data that could be read from the
+// file before the error occurred. When the file size exceeds |max_size|, the
+// function returns false with |contents| holding the file truncated to
+// |max_size|.
+// |contents| may be NULL, in which case this function is useful for its side
+// effect of priming the disk cache (could be used for unit tests).
+BASE_EXPORT bool ReadFileToString(const FilePath& path,
+ std::string* contents,
+ size_t max_size);
+
#if defined(OS_POSIX)
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
@@ -153,7 +174,7 @@ BASE_EXPORT bool CreateSymbolicLink(const FilePath& target,
// Returns false upon failure.
BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
-// Bits ans masks of the file permission.
+// Bits and masks of the file permission.
enum FilePermissionBits {
FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO,
FILE_PERMISSION_USER_MASK = S_IRWXU,
@@ -192,25 +213,13 @@ BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path);
// they're open (which can lead to security issues).
BASE_EXPORT bool GetTempDir(FilePath* path);
-// Get a temporary directory for shared memory files. The directory may depend
-// on whether the destination is intended for executable files, which in turn
-// depends on how /dev/shmem was mounted. As a result, you must supply whether
-// you intend to create executable shmem segments so this function can find
-// an appropriate location.
-//
-// Only useful on POSIX; redirects to GetTempDir() on Windows.
-BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path);
-
-#if defined(OS_POSIX)
-// Get the home directory. This is more complicated than just getenv("HOME")
+// Get the home directory. This is more complicated than just getenv("HOME")
// as it knows to fall back on getpwent() etc.
//
-// This function is not currently implemented on Windows or Mac because we
-// don't use it. Generally you would use one of PathService's APP_DATA
-// directories on those platforms. If we need it, this could be implemented
-// there to return the appropriate directory.
+// You should not generally call this directly. Instead use DIR_HOME with the
+// path service which will use this function but cache the value.
+// Path service may also override DIR_HOME.
BASE_EXPORT FilePath GetHomeDir();
-#endif // OS_POSIX
// 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
@@ -226,12 +235,6 @@ BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir,
// Returns a handle to the opened file or NULL if an error occurred.
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);
@@ -256,7 +259,7 @@ BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,
// Returns true on success, leaving *error unchanged.
// Returns false on failure and sets *error appropriately, if it is non-NULL.
BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,
- PlatformFileError* error);
+ File::Error* error);
// Backward-compatible convenience method for the above.
BASE_EXPORT bool CreateDirectory(const FilePath& full_path);
@@ -292,7 +295,7 @@ BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path,
BASE_EXPORT bool IsLink(const FilePath& file_path);
// Returns information about the given file path.
-BASE_EXPORT bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* info);
+BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info);
// Sets the time of the last access and the time of the last modification.
BASE_EXPORT bool TouchFile(const FilePath& path,
@@ -305,52 +308,45 @@ 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);
+// Associates a standard FILE stream with an existing File. Note that this
+// functions take ownership of the existing File.
+BASE_EXPORT FILE* FileToFILE(File file, const char* mode);
+
// 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);
-// 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 base::FilePath& filename, char* data, int size);
-
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
+// Reads at most 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 max_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 base::FilePath& filename, const char* data,
+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 base::FilePath& filename,
+BASE_EXPORT int AppendToFile(const FilePath& filename,
const char* data, int size);
// Gets the current working directory for the process.
-BASE_EXPORT bool GetCurrentDirectory(base::FilePath* path);
+BASE_EXPORT bool GetCurrentDirectory(FilePath* path);
// Sets the current working directory for the process.
-BASE_EXPORT bool SetCurrentDirectory(const base::FilePath& path);
+BASE_EXPORT bool SetCurrentDirectory(const FilePath& path);
// 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 base::FilePath& path,
- const base::FilePath::StringType& suffix);
-
-#if defined(OS_POSIX)
-// Creates a directory with a guaranteed unique name based on |path|, returning
-// the pathname if successful, or an empty path if there was an error creating
-// the directory. Does not create parent directories.
-BASE_EXPORT base::FilePath MakeUniqueDirectory(const base::FilePath& path);
-#endif
+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
@@ -385,33 +381,6 @@ BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path);
// the directory |path|, in the number of FilePath::CharType, or -1 on failure.
BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path);
-// 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;
-
-#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 (IGNORE_EINTR(close(*x)) < 0)
- DPLOG(ERROR) << "close";
- }
- }
-};
-
-typedef scoped_ptr_malloc<int, ScopedFDClose> ScopedFD;
-#endif // OS_POSIX
-
#if defined(OS_LINUX)
// Broad categories of file systems as returned by statfs() on Linux.
enum FileSystemType {
@@ -429,10 +398,35 @@ enum FileSystemType {
// Attempts determine the FileSystemType for |path|.
// Returns false if |path| doesn't exist.
-BASE_EXPORT bool GetFileSystemType(const base::FilePath& path,
- FileSystemType* type);
+BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type);
#endif
+#if defined(OS_POSIX)
+// Get a temporary directory for shared memory files. The directory may depend
+// on whether the destination is intended for executable files, which in turn
+// depends on how /dev/shmem was mounted. As a result, you must supply whether
+// you intend to create executable shmem segments so this function can find
+// an appropriate location.
+BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path);
+#endif
+
+} // namespace base
+
+// -----------------------------------------------------------------------------
+
+namespace file_util {
+
+// Functor for |ScopedFILE| (below).
+struct ScopedFILEClose {
+ inline void operator()(FILE* x) const {
+ if (x)
+ fclose(x);
+ }
+};
+
+// Automatically closes |FILE*|s.
+typedef scoped_ptr<FILE, ScopedFILEClose> ScopedFILE;
+
} // namespace file_util
// Internal --------------------------------------------------------------------
diff --git a/chromium/base/file_util_linux.cc b/chromium/base/file_util_linux.cc
index e4f0e28b129..2910c9cb4ec 100644
--- a/chromium/base/file_util_linux.cc
+++ b/chromium/base/file_util_linux.cc
@@ -4,14 +4,30 @@
#include "base/file_util.h"
-#include "base/files/file_path.h"
-
#include <errno.h>
+#include <linux/magic.h>
#include <sys/vfs.h>
-namespace file_util {
+#include "base/files/file_path.h"
+
+// Make sure some of the newer macros from magic.h are defined.
+// TODO(mostynb@opera.com): remove this after 2014.
+#ifndef BTRFS_SUPER_MAGIC
+#define BTRFS_SUPER_MAGIC 0x9123683E
+#endif
+#ifndef HUGETLBFS_MAGIC
+#define HUGETLBFS_MAGIC 0x958458f6
+#endif
+#ifndef RAMFS_MAGIC
+#define RAMFS_MAGIC 0x858458f6
+#endif
+#ifndef TMPFS_MAGIC
+#define TMPFS_MAGIC 0x01021994
+#endif
+
+namespace base {
-bool GetFileSystemType(const base::FilePath& path, FileSystemType* type) {
+bool GetFileSystemType(const FilePath& path, FileSystemType* type) {
struct statfs statfs_buf;
if (statfs(path.value().c_str(), &statfs_buf) < 0) {
if (errno == ENOENT)
@@ -20,37 +36,37 @@ bool GetFileSystemType(const base::FilePath& path, FileSystemType* type) {
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.
+ // Not all possible |statfs_buf.f_type| values are in linux/magic.h.
+ // Missing 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 EXT2_SUPER_MAGIC: // Also ext3 and ext4
+ case MSDOS_SUPER_MAGIC:
+ case REISERFS_SUPER_MAGIC:
+ case BTRFS_SUPER_MAGIC:
+ case 0x5346544E: // NTFS
case 0x58465342: // XFS
- case 0x9123683E: // btrfs
case 0x3153464A: // JFS
*type = FILE_SYSTEM_ORDINARY;
break;
- case 0x6969: // NFS
+ case NFS_SUPER_MAGIC:
*type = FILE_SYSTEM_NFS;
break;
+ case SMB_SUPER_MAGIC:
case 0xFF534D42: // CIFS
- case 0x517B: // SMB
*type = FILE_SYSTEM_SMB;
break;
- case 0x73757245: // Coda
+ case CODA_SUPER_MAGIC:
*type = FILE_SYSTEM_CODA;
break;
- case 0x858458f6: // ramfs
- case 0x01021994: // tmpfs
+ case HUGETLBFS_MAGIC:
+ case RAMFS_MAGIC:
+ case TMPFS_MAGIC:
*type = FILE_SYSTEM_MEMORY;
break;
- case 0x27e0eb: // CGROUP
+ case CGROUP_SUPER_MAGIC:
*type = FILE_SYSTEM_CGROUP;
break;
default:
@@ -59,4 +75,4 @@ bool GetFileSystemType(const base::FilePath& path, FileSystemType* type) {
return true;
}
-} // namespace
+} // namespace base
diff --git a/chromium/base/file_util_mac.mm b/chromium/base/file_util_mac.mm
index f8a6b6301f5..4aa6d552a76 100644
--- a/chromium/base/file_util_mac.mm
+++ b/chromium/base/file_util_mac.mm
@@ -19,7 +19,7 @@ namespace internal {
bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
ThreadRestrictions::AssertIOAllowed();
return (copyfile(from_path.value().c_str(),
- to_path.value().c_str(), NULL, COPYFILE_ALL) == 0);
+ to_path.value().c_str(), NULL, COPYFILE_DATA) == 0);
}
} // namespace internal
@@ -32,8 +32,21 @@ bool GetTempDir(base::FilePath* path) {
return true;
}
-bool GetShmemTempDir(bool executable, base::FilePath* path) {
- return GetTempDir(path);
+FilePath GetHomeDir() {
+ NSString* tmp = NSHomeDirectory();
+ if (tmp != nil) {
+ FilePath mac_home_dir = base::mac::NSStringToFilePath(tmp);
+ if (!mac_home_dir.empty())
+ return mac_home_dir;
+ }
+
+ // Fall back on temp dir if no home directory is defined.
+ FilePath rv;
+ if (GetTempDir(&rv))
+ return rv;
+
+ // Last resort.
+ return FilePath("/tmp");
}
} // namespace base
diff --git a/chromium/base/file_util_posix.cc b/chromium/base/file_util_posix.cc
index ddcd5ddb912..92e8cad1605 100644
--- a/chromium/base/file_util_posix.cc
+++ b/chromium/base/file_util_posix.cc
@@ -33,6 +33,7 @@
#include "base/basictypes.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
@@ -60,8 +61,7 @@ namespace base {
namespace {
-#if defined(OS_BSD) || defined(OS_MACOSX)
-typedef struct stat stat_wrapper_t;
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
static int CallStat(const char *path, stat_wrapper_t *sb) {
ThreadRestrictions::AssertIOAllowed();
return stat(path, sb);
@@ -70,8 +70,7 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) {
ThreadRestrictions::AssertIOAllowed();
return lstat(path, sb);
}
-#else
-typedef struct stat64 stat_wrapper_t;
+#else // defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
static int CallStat(const char *path, stat_wrapper_t *sb) {
ThreadRestrictions::AssertIOAllowed();
return stat64(path, sb);
@@ -80,13 +79,7 @@ static int CallLstat(const char *path, stat_wrapper_t *sb) {
ThreadRestrictions::AssertIOAllowed();
return lstat64(path, sb);
}
-#if defined(OS_ANDROID)
-static int CallFstat(int fd, stat_wrapper_t *sb) {
- ThreadRestrictions::AssertIOAllowed();
- return fstat64(fd, sb);
-}
-#endif
-#endif
+#endif // !(defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL))
// Helper for NormalizeFilePath(), defined below.
bool RealPath(const FilePath& path, FilePath* real_path) {
@@ -172,15 +165,15 @@ int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
bool DetermineDevShmExecutable() {
bool result = false;
FilePath path;
- int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path);
- if (fd >= 0) {
- file_util::ScopedFD shm_fd_closer(&fd);
+
+ ScopedFD fd(CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path));
+ if (fd.is_valid()) {
DeleteFile(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);
+ void* mapping = mmap(NULL, pagesize, PROT_READ, MAP_SHARED, fd.get(), 0);
if (mapping != MAP_FAILED) {
if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
result = true;
@@ -244,12 +237,12 @@ bool DeleteFile(const FilePath& path, bool recursive) {
bool ReplaceFile(const FilePath& from_path,
const FilePath& to_path,
- PlatformFileError* error) {
+ File::Error* error) {
ThreadRestrictions::AssertIOAllowed();
if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0)
return true;
if (error)
- *error = ErrnoToPlatformFileError(errno);
+ *error = File::OSErrorToFileError(errno);
return false;
}
@@ -260,13 +253,10 @@ bool CopyDirectory(const FilePath& from_path,
// 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 (strlcpy(top_dir, from_path.value().c_str(),
- arraysize(top_dir)) >= arraysize(top_dir)) {
+ if (from_path.value().size() >= PATH_MAX) {
return false;
}
@@ -286,10 +276,10 @@ bool CopyDirectory(const FilePath& 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)
+ 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;
@@ -302,7 +292,7 @@ bool CopyDirectory(const FilePath& from_path,
if (stat(from_path.value().c_str(), &from_stat) < 0) {
DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
<< from_path.value() << " errno = " << errno;
- success = false;
+ return false;
}
struct stat to_path_stat;
FilePath from_path_base = from_path;
@@ -315,8 +305,10 @@ bool CopyDirectory(const FilePath& from_path,
// The Windows version of this function assumes that non-recursive calls
// will always have a directory for from_path.
+ // TODO(maruel): This is not necessary anymore.
DCHECK(recursive || S_ISDIR(from_stat.st_mode));
+ bool success = true;
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.
@@ -463,30 +455,14 @@ bool GetTempDir(FilePath* path) {
}
#endif // !defined(OS_MACOSX)
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
-// This is implemented in file_util_mac.mm and file_util_android.cc for those
-// platforms.
-bool GetShmemTempDir(bool executable, FilePath* path) {
-#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_MACOSX) && !defined(OS_ANDROID)
-
-#if !defined(OS_MACOSX)
+#if !defined(OS_MACOSX) // Mac implementation is in file_util_mac.mm.
FilePath GetHomeDir() {
#if defined(OS_CHROMEOS)
- if (SysInfo::IsRunningOnChromeOS())
- return FilePath("/home/chronos/user");
+ if (SysInfo::IsRunningOnChromeOS()) {
+ // On Chrome OS chrome::DIR_USER_DATA is overridden with a primary user
+ // homedir once it becomes available. Return / as the safe option.
+ return FilePath("/");
+ }
#endif
const char* home_dir = getenv("HOME");
@@ -496,9 +472,12 @@ FilePath GetHomeDir() {
#if defined(OS_ANDROID)
DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
#elif defined(USE_GLIB) && !defined(OS_CHROMEOS)
- // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
- ThreadRestrictions::AssertIOAllowed();
-
+ // g_get_home_dir calls getpwent, which can fall through to LDAP calls so
+ // this may do I/O. However, it should be rare that $HOME is not defined and
+ // this is typically called from the path service which has no threading
+ // restrictions. The path service will cache the result which limits the
+ // badness of blocking on I/O. As a result, we don't have a thread
+ // restriction here.
home_dir = g_get_home_dir();
if (home_dir && home_dir[0])
return FilePath(home_dir);
@@ -525,14 +504,6 @@ bool CreateTemporaryFile(FilePath* path) {
return true;
}
-FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
- FilePath directory;
- if (!GetShmemTempDir(executable, &directory))
- return NULL;
-
- return CreateAndOpenTemporaryFileInDir(directory, path);
-}
-
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
int fd = CreateAndOpenFdForTemporaryFile(dir, path);
if (fd < 0)
@@ -589,7 +560,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix,
}
bool CreateDirectoryAndGetError(const FilePath& full_path,
- PlatformFileError* error) {
+ File::Error* error) {
ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
std::vector<FilePath> subpaths;
@@ -616,7 +587,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
int saved_errno = errno;
if (!DirectoryExists(*i)) {
if (error)
- *error = ErrnoToPlatformFileError(saved_errno);
+ *error = File::OSErrorToFileError(saved_errno);
return false;
}
}
@@ -654,16 +625,14 @@ bool IsLink(const FilePath& file_path) {
return false;
}
-bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) {
+bool GetFileInfo(const FilePath& file_path, File::Info* results) {
stat_wrapper_t file_info;
#if defined(OS_ANDROID)
if (file_path.IsContentUri()) {
- int fd = OpenContentUriForRead(file_path);
- if (fd < 0)
- return false;
- file_util::ScopedFD scoped_fd(&fd);
- if (CallFstat(fd, &file_info) != 0)
+ File file = OpenContentUriForRead(file_path);
+ if (!file.IsValid())
return false;
+ return file.GetInfo(results);
} else {
#endif // defined(OS_ANDROID)
if (CallStat(file_path.value().c_str(), &file_info) != 0)
@@ -671,21 +640,8 @@ bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) {
#if defined(OS_ANDROID)
}
#endif // defined(OS_ANDROID)
- results->is_directory = S_ISDIR(file_info.st_mode);
- results->size = file_info.st_size;
-#if defined(OS_MACOSX)
- results->last_modified = Time::FromTimeSpec(file_info.st_mtimespec);
- results->last_accessed = Time::FromTimeSpec(file_info.st_atimespec);
- results->creation_time = Time::FromTimeSpec(file_info.st_ctimespec);
-#elif defined(OS_ANDROID)
- results->last_modified = Time::FromTimeT(file_info.st_mtime);
- results->last_accessed = Time::FromTimeT(file_info.st_atime);
- results->creation_time = Time::FromTimeT(file_info.st_ctime);
-#else
- results->last_modified = Time::FromTimeSpec(file_info.st_mtim);
- results->last_accessed = Time::FromTimeSpec(file_info.st_atim);
- results->creation_time = Time::FromTimeSpec(file_info.st_ctim);
-#endif
+
+ results->FromStat(file_info);
return true;
}
@@ -698,65 +654,37 @@ FILE* OpenFile(const FilePath& filename, const char* mode) {
return result;
}
-int ReadFile(const FilePath& filename, char* data, int size) {
+// NaCl doesn't implement system calls to open files directly.
+#if !defined(OS_NACL)
+FILE* FileToFILE(File file, const char* mode) {
+ FILE* stream = fdopen(file.GetPlatformFile(), mode);
+ if (stream)
+ file.TakePlatformFile();
+ return stream;
+}
+#endif // !defined(OS_NACL)
+
+int ReadFile(const FilePath& filename, char* data, int max_size) {
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 = IGNORE_EINTR(close(fd)) < 0)
- return ret;
+ ssize_t bytes_read = HANDLE_EINTR(read(fd, data, max_size));
+ if (IGNORE_EINTR(close(fd)) < 0)
+ return -1;
return bytes_read;
}
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::stat_wrapper_t;
-using base::CallStat;
-using base::CallLstat;
-using base::CreateAndOpenFdForTemporaryFile;
-using base::DirectoryExists;
-using base::FileEnumerator;
-using base::FilePath;
-using base::MakeAbsoluteFilePath;
-using base::VerifySpecificPathControlledByUser;
-
-base::FilePath MakeUniqueDirectory(const base::FilePath& path) {
- const int kMaxAttempts = 20;
- for (int attempts = 0; attempts < kMaxAttempts; attempts++) {
- int uniquifier =
- GetUniquePathNumber(path, base::FilePath::StringType());
- if (uniquifier < 0)
- break;
- base::FilePath test_path = (uniquifier == 0) ? path :
- path.InsertBeforeExtensionASCII(
- base::StringPrintf(" (%d)", uniquifier));
- if (mkdir(test_path.value().c_str(), 0777) == 0)
- return test_path;
- else if (errno != EEXIST)
- break;
- }
- return base::FilePath();
-}
-
-FILE* OpenFile(const std::string& filename, const char* mode) {
- return OpenFile(FilePath(filename), mode);
-}
-
int WriteFile(const FilePath& filename, const char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
- int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0666));
+ ThreadRestrictions::AssertIOAllowed();
+ int fd = HANDLE_EINTR(creat(filename.value().c_str(), 0640));
if (fd < 0)
return -1;
int bytes_written = WriteFileDescriptor(fd, data, size);
- if (int ret = IGNORE_EINTR(close(fd)) < 0)
- return ret;
+ if (IGNORE_EINTR(close(fd)) < 0)
+ return -1;
return bytes_written;
}
@@ -776,21 +704,21 @@ int WriteFileDescriptor(const int fd, const char* data, int size) {
}
int AppendToFile(const FilePath& filename, const char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ 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 = IGNORE_EINTR(close(fd)) < 0)
- return ret;
+ if (IGNORE_EINTR(close(fd)) < 0)
+ return -1;
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();
+ ThreadRestrictions::AssertIOAllowed();
char system_buffer[PATH_MAX] = "";
if (!getcwd(system_buffer, sizeof(system_buffer))) {
@@ -803,7 +731,7 @@ bool GetCurrentDirectory(FilePath* dir) {
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
int ret = chdir(path.value().c_str());
return !ret;
}
@@ -859,7 +787,7 @@ bool VerifyPathControlledByAdmin(const FilePath& path) {
};
// Reading the groups database may touch the file system.
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
std::set<gid_t> allowed_group_ids;
for (int i = 0, ie = arraysize(kAdminGroupNames); i < ie; ++i) {
@@ -879,13 +807,30 @@ bool VerifyPathControlledByAdmin(const FilePath& path) {
#endif // defined(OS_MACOSX) && !defined(OS_IOS)
int GetMaximumPathComponentLength(const FilePath& path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
return pathconf(path.value().c_str(), _PC_NAME_MAX);
}
-} // namespace file_util
+#if !defined(OS_ANDROID)
+// This is implemented in file_util_android.cc for that platform.
+bool GetShmemTempDir(bool executable, FilePath* path) {
+#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)
+
+// -----------------------------------------------------------------------------
-namespace base {
namespace internal {
bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
diff --git a/chromium/base/file_util_unittest.cc b/chromium/base/file_util_unittest.cc
index b65e171719f..239563d304e 100644
--- a/chromium/base/file_util_unittest.cc
+++ b/chromium/base/file_util_unittest.cc
@@ -12,14 +12,22 @@
#include <winioctl.h>
#endif
+#if defined(OS_POSIX)
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#endif
+
#include <algorithm>
#include <fstream>
#include <set>
+#include <vector>
#include "base/base_paths.h"
#include "base/file_util.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
@@ -485,25 +493,6 @@ TEST_F(FileUtilTest, DevicePathToDriveLetter) {
&win32_path));
}
-TEST_F(FileUtilTest, GetPlatformFileInfoForDirectory) {
- FilePath empty_dir = temp_dir_.path().Append(FPL("gpfi_test"));
- ASSERT_TRUE(CreateDirectory(empty_dir));
- 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());
- PlatformFileInfo info;
- EXPECT_TRUE(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:
@@ -643,6 +632,17 @@ TEST_F(FileUtilTest, DeleteNonExistent) {
ASSERT_FALSE(PathExists(non_existent));
}
+TEST_F(FileUtilTest, DeleteNonExistentWithNonExistentParent) {
+ FilePath non_existent = temp_dir_.path().AppendASCII("bogus_topdir");
+ non_existent = non_existent.AppendASCII("bogus_subdir");
+ ASSERT_FALSE(PathExists(non_existent));
+
+ EXPECT_TRUE(DeleteFile(non_existent, false));
+ ASSERT_FALSE(PathExists(non_existent));
+ EXPECT_TRUE(DeleteFile(non_existent, true));
+ ASSERT_FALSE(PathExists(non_existent));
+}
+
TEST_F(FileUtilTest, DeleteFile) {
// Create a file
FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteFile 1.txt"));
@@ -716,7 +716,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
// Write file.
EXPECT_EQ(static_cast<int>(kData.length()),
- file_util::WriteFile(file_name, kData.data(), kData.length()));
+ WriteFile(file_name, kData.data(), kData.length()));
EXPECT_TRUE(PathExists(file_name));
// Make sure the file is readable.
@@ -755,7 +755,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
// Write file.
EXPECT_EQ(static_cast<int>(kData.length()),
- file_util::WriteFile(file_name, kData.data(), kData.length()));
+ WriteFile(file_name, kData.data(), kData.length()));
EXPECT_TRUE(PathExists(file_name));
// Make sure the file is writable.
@@ -769,8 +769,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
EXPECT_FALSE(mode & 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_EQ(-1, WriteFile(file_name, kData.data(), kData.length()));
EXPECT_FALSE(PathIsWritable(file_name));
// Give read permission.
@@ -780,7 +779,7 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
EXPECT_TRUE(mode & 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()));
+ WriteFile(file_name, kData.data(), kData.length()));
EXPECT_TRUE(PathIsWritable(file_name));
// Delete the file.
@@ -800,7 +799,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
EXPECT_FALSE(PathExists(file_name));
const std::string kData("hello");
EXPECT_EQ(static_cast<int>(kData.length()),
- file_util::WriteFile(file_name, kData.data(), kData.length()));
+ WriteFile(file_name, kData.data(), kData.length()));
EXPECT_TRUE(PathExists(file_name));
// Make sure the directory has the all permissions.
@@ -817,7 +816,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
FileEnumerator f1(subdir_path, true, FileEnumerator::FILES);
EXPECT_TRUE(PathExists(subdir_path));
FindResultCollector c1(f1);
- EXPECT_EQ(c1.size(), 0);
+ EXPECT_EQ(0, c1.size());
EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode));
// Give the permissions to the directory.
@@ -829,7 +828,7 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
FileEnumerator f2(subdir_path, true, FileEnumerator::FILES);
FindResultCollector c2(f2);
EXPECT_TRUE(c2.HasFile(file_name));
- EXPECT_EQ(c2.size(), 1);
+ EXPECT_EQ(1, c2.size());
// Delete the file.
EXPECT_TRUE(DeleteFile(subdir_path, true));
@@ -1379,6 +1378,60 @@ TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) {
EXPECT_TRUE(PathExists(file_name_to));
}
+// Sets the source file to read-only.
+void SetReadOnly(const FilePath& path) {
+#if defined(OS_WIN)
+ // On Windows, it involves setting a bit.
+ DWORD attrs = GetFileAttributes(path.value().c_str());
+ ASSERT_NE(INVALID_FILE_ATTRIBUTES, attrs);
+ ASSERT_TRUE(SetFileAttributes(
+ path.value().c_str(), attrs | FILE_ATTRIBUTE_READONLY));
+ attrs = GetFileAttributes(path.value().c_str());
+ // Files in the temporary directory should not be indexed ever. If this
+ // assumption change, fix this unit test accordingly.
+ // FILE_ATTRIBUTE_NOT_CONTENT_INDEXED doesn't exist on XP.
+ DWORD expected = FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_READONLY;
+ if (win::GetVersion() >= win::VERSION_VISTA)
+ expected |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED;
+ ASSERT_EQ(expected, attrs);
+#else
+ // On all other platforms, it involves removing the write bit.
+ EXPECT_TRUE(SetPosixFilePermissions(path, S_IRUSR));
+#endif
+}
+
+bool IsReadOnly(const FilePath& path) {
+#if defined(OS_WIN)
+ DWORD attrs = GetFileAttributes(path.value().c_str());
+ EXPECT_NE(INVALID_FILE_ATTRIBUTES, attrs);
+ return attrs & FILE_ATTRIBUTE_READONLY;
+#else
+ int mode = 0;
+ EXPECT_TRUE(GetPosixFilePermissions(path, &mode));
+ return !(mode & S_IWUSR);
+#endif
+}
+
+TEST_F(FileUtilTest, CopyDirectoryACL) {
+ // Create a directory.
+ FilePath src = temp_dir_.path().Append(FILE_PATH_LITERAL("src"));
+ CreateDirectory(src);
+ ASSERT_TRUE(PathExists(src));
+
+ // Create a file under the directory.
+ FilePath src_file = src.Append(FILE_PATH_LITERAL("src.txt"));
+ CreateTextFile(src_file, L"Gooooooooooooooooooooogle");
+ SetReadOnly(src_file);
+ ASSERT_TRUE(IsReadOnly(src_file));
+
+ // Copy the directory recursively.
+ FilePath dst = temp_dir_.path().Append(FILE_PATH_LITERAL("dst"));
+ FilePath dst_file = dst.Append(FILE_PATH_LITERAL("src.txt"));
+ EXPECT_TRUE(CopyDirectory(src, dst, true));
+
+ ASSERT_FALSE(IsReadOnly(dst_file));
+}
+
TEST_F(FileUtilTest, CopyFile) {
// Create a directory
FilePath dir_name_from =
@@ -1417,6 +1470,27 @@ TEST_F(FileUtilTest, CopyFile) {
EXPECT_TRUE(PathExists(dest_file2));
}
+TEST_F(FileUtilTest, CopyFileACL) {
+ // While FileUtilTest.CopyFile asserts the content is correctly copied over,
+ // this test case asserts the access control bits are meeting expectations in
+ // CopyFileUnsafe().
+ FilePath src = temp_dir_.path().Append(FILE_PATH_LITERAL("src.txt"));
+ const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+ CreateTextFile(src, file_contents);
+
+ // Set the source file to read-only.
+ ASSERT_FALSE(IsReadOnly(src));
+ SetReadOnly(src);
+ ASSERT_TRUE(IsReadOnly(src));
+
+ // Copy the file.
+ FilePath dst = temp_dir_.path().Append(FILE_PATH_LITERAL("dst.txt"));
+ ASSERT_TRUE(CopyFile(src, dst));
+ EXPECT_EQ(file_contents, ReadTextFile(dst));
+
+ ASSERT_FALSE(IsReadOnly(dst));
+}
+
// file_util winds up using autoreleased objects on the Mac, so this needs
// to be a PlatformTest.
typedef PlatformTest ReadOnlyFileUtilTest;
@@ -1611,6 +1685,21 @@ TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
}
}
+TEST_F(FileUtilTest, FileToFILE) {
+ File file;
+ FILE* stream = FileToFILE(file.Pass(), "w");
+ EXPECT_FALSE(stream);
+
+ FilePath file_name = temp_dir_.path().Append(FPL("The file.txt"));
+ file = File(file_name, File::FLAG_CREATE | File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+
+ stream = FileToFILE(file.Pass(), "w");
+ EXPECT_TRUE(stream);
+ EXPECT_FALSE(file.IsValid());
+ EXPECT_TRUE(CloseFile(stream));
+}
+
TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
FilePath temp_dir;
ASSERT_TRUE(CreateNewTempDirectory(FilePath::StringType(), &temp_dir));
@@ -1629,11 +1718,24 @@ TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) {
EXPECT_TRUE(DeleteFile(new_dir, false));
}
+#if defined(OS_POSIX)
TEST_F(FileUtilTest, GetShmemTempDirTest) {
FilePath dir;
EXPECT_TRUE(GetShmemTempDir(false, &dir));
EXPECT_TRUE(DirectoryExists(dir));
}
+#endif
+
+TEST_F(FileUtilTest, GetHomeDirTest) {
+#if !defined(OS_ANDROID) // Not implemented on Android.
+ // We don't actually know what the home directory is supposed to be without
+ // calling some OS functions which would just duplicate the implementation.
+ // So here we just test that it returns something "reasonable".
+ FilePath home = GetHomeDir();
+ ASSERT_FALSE(home.empty());
+ ASSERT_TRUE(home.IsAbsolute());
+#endif
+}
TEST_F(FileUtilTest, CreateDirectoryTest) {
FilePath test_root =
@@ -1712,8 +1814,8 @@ TEST_F(FileUtilTest, DetectDirectoryTest) {
TEST_F(FileUtilTest, FileEnumeratorTest) {
// Test an empty directory.
FileEnumerator f0(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
- EXPECT_EQ(f0.Next().value(), FPL(""));
- EXPECT_EQ(f0.Next().value(), FPL(""));
+ EXPECT_EQ(FPL(""), f0.Next().value());
+ EXPECT_EQ(FPL(""), f0.Next().value());
// Test an empty directory, non-recursively, including "..".
FileEnumerator f0_dotdot(temp_dir_.path(), false,
@@ -1749,7 +1851,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c1.HasFile(file2_abs));
EXPECT_TRUE(c1.HasFile(dir2file));
EXPECT_TRUE(c1.HasFile(dir2innerfile));
- EXPECT_EQ(c1.size(), 4);
+ EXPECT_EQ(4, c1.size());
// Only enumerate directories.
FileEnumerator f2(temp_dir_.path(), true, FileEnumerator::DIRECTORIES);
@@ -1757,7 +1859,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c2.HasFile(dir1));
EXPECT_TRUE(c2.HasFile(dir2));
EXPECT_TRUE(c2.HasFile(dir2inner));
- EXPECT_EQ(c2.size(), 3);
+ EXPECT_EQ(3, c2.size());
// Only enumerate directories non-recursively.
FileEnumerator f2_non_recursive(
@@ -1765,7 +1867,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
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);
+ EXPECT_EQ(2, c2_non_recursive.size());
// Only enumerate directories, non-recursively, including "..".
FileEnumerator f2_dotdot(temp_dir_.path(), false,
@@ -1775,7 +1877,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c2_dotdot.HasFile(dir1));
EXPECT_TRUE(c2_dotdot.HasFile(dir2));
EXPECT_TRUE(c2_dotdot.HasFile(temp_dir_.path().Append(FPL(".."))));
- EXPECT_EQ(c2_dotdot.size(), 3);
+ EXPECT_EQ(3, c2_dotdot.size());
// Enumerate files and directories.
FileEnumerator f3(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
@@ -1787,7 +1889,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c3.HasFile(dir2file));
EXPECT_TRUE(c3.HasFile(dir2inner));
EXPECT_TRUE(c3.HasFile(dir2innerfile));
- EXPECT_EQ(c3.size(), 7);
+ EXPECT_EQ(7, c3.size());
// Non-recursive operation.
FileEnumerator f4(temp_dir_.path(), false, FILES_AND_DIRECTORIES);
@@ -1796,7 +1898,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c4.HasFile(dir2));
EXPECT_TRUE(c4.HasFile(file1));
EXPECT_TRUE(c4.HasFile(file2_abs));
- EXPECT_EQ(c4.size(), 4);
+ EXPECT_EQ(4, c4.size());
// Enumerate with a pattern.
FileEnumerator f5(temp_dir_.path(), true, FILES_AND_DIRECTORIES, FPL("dir*"));
@@ -1806,7 +1908,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c5.HasFile(dir2file));
EXPECT_TRUE(c5.HasFile(dir2inner));
EXPECT_TRUE(c5.HasFile(dir2innerfile));
- EXPECT_EQ(c5.size(), 5);
+ EXPECT_EQ(5, c5.size());
#if defined(OS_WIN)
{
@@ -1824,7 +1926,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c6.HasFile(inner2));
EXPECT_TRUE(c6.HasFile(inner2.Append(FPL("innerfile.txt"))));
EXPECT_TRUE(c6.HasFile(dir1.Append(FPL("dir2file.txt"))));
- EXPECT_EQ(c6.size(), 3);
+ EXPECT_EQ(3, c6.size());
}
// No changes for non recursive operation.
@@ -1834,7 +1936,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c7.HasFile(dir2));
EXPECT_TRUE(c7.HasFile(file1));
EXPECT_TRUE(c7.HasFile(file2_abs));
- EXPECT_EQ(c7.size(), 4);
+ EXPECT_EQ(4, c7.size());
// Should not enumerate inside dir1 when using recursion.
FileEnumerator f8(temp_dir_.path(), true, FILES_AND_DIRECTORIES);
@@ -1846,7 +1948,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
EXPECT_TRUE(c8.HasFile(dir2file));
EXPECT_TRUE(c8.HasFile(dir2inner));
EXPECT_TRUE(c8.HasFile(dir2innerfile));
- EXPECT_EQ(c8.size(), 7);
+ EXPECT_EQ(7, c8.size());
}
#endif
@@ -1875,16 +1977,117 @@ TEST_F(FileUtilTest, AppendToFile) {
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(-1, AppendToFile(foobar, data.c_str(), data.length()));
EXPECT_EQ(static_cast<int>(data.length()),
- file_util::WriteFile(foobar, data.c_str(), data.length()));
+ WriteFile(foobar, data.c_str(), data.length()));
EXPECT_EQ(static_cast<int>(data.length()),
- file_util::AppendToFile(foobar, data.c_str(), data.length()));
+ AppendToFile(foobar, data.c_str(), data.length()));
const std::wstring read_content = ReadTextFile(foobar);
EXPECT_EQ(L"hellohello", read_content);
}
+TEST_F(FileUtilTest, ReadFile) {
+ // Create a test file to be read.
+ const std::string kTestData("The quick brown fox jumps over the lazy dog.");
+ FilePath file_path =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileTest"));
+
+ ASSERT_EQ(static_cast<int>(kTestData.size()),
+ WriteFile(file_path, kTestData.data(), kTestData.size()));
+
+ // Make buffers with various size.
+ std::vector<char> small_buffer(kTestData.size() / 2);
+ std::vector<char> exact_buffer(kTestData.size());
+ std::vector<char> large_buffer(kTestData.size() * 2);
+
+ // Read the file with smaller buffer.
+ int bytes_read_small = ReadFile(
+ file_path, &small_buffer[0], static_cast<int>(small_buffer.size()));
+ EXPECT_EQ(static_cast<int>(small_buffer.size()), bytes_read_small);
+ EXPECT_EQ(
+ std::string(kTestData.begin(), kTestData.begin() + small_buffer.size()),
+ std::string(small_buffer.begin(), small_buffer.end()));
+
+ // Read the file with buffer which have exactly same size.
+ int bytes_read_exact = ReadFile(
+ file_path, &exact_buffer[0], static_cast<int>(exact_buffer.size()));
+ EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_exact);
+ EXPECT_EQ(kTestData, std::string(exact_buffer.begin(), exact_buffer.end()));
+
+ // Read the file with larger buffer.
+ int bytes_read_large = ReadFile(
+ file_path, &large_buffer[0], static_cast<int>(large_buffer.size()));
+ EXPECT_EQ(static_cast<int>(kTestData.size()), bytes_read_large);
+ EXPECT_EQ(kTestData, std::string(large_buffer.begin(),
+ large_buffer.begin() + kTestData.size()));
+
+ // Make sure the return value is -1 if the file doesn't exist.
+ FilePath file_path_not_exist =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileNotExistTest"));
+ EXPECT_EQ(-1,
+ ReadFile(file_path_not_exist,
+ &exact_buffer[0],
+ static_cast<int>(exact_buffer.size())));
+}
+
+TEST_F(FileUtilTest, ReadFileToString) {
+ const char kTestData[] = "0123";
+ std::string data;
+
+ FilePath file_path =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("ReadFileToStringTest"));
+ FilePath file_path_dangerous =
+ temp_dir_.path().Append(FILE_PATH_LITERAL("..")).
+ Append(temp_dir_.path().BaseName()).
+ Append(FILE_PATH_LITERAL("ReadFileToStringTest"));
+
+ // Create test file.
+ ASSERT_EQ(4, WriteFile(file_path, kTestData, 4));
+
+ EXPECT_TRUE(ReadFileToString(file_path, &data));
+ EXPECT_EQ(kTestData, data);
+
+ data = "temp";
+ EXPECT_FALSE(ReadFileToString(file_path, &data, 0));
+ EXPECT_EQ(0u, data.length());
+
+ data = "temp";
+ EXPECT_FALSE(ReadFileToString(file_path, &data, 2));
+ EXPECT_EQ("01", data);
+
+ data.clear();
+ EXPECT_FALSE(ReadFileToString(file_path, &data, 3));
+ EXPECT_EQ("012", data);
+
+ data.clear();
+ EXPECT_TRUE(ReadFileToString(file_path, &data, 4));
+ EXPECT_EQ("0123", data);
+
+ data.clear();
+ EXPECT_TRUE(ReadFileToString(file_path, &data, 6));
+ EXPECT_EQ("0123", data);
+
+ EXPECT_TRUE(ReadFileToString(file_path, NULL, 6));
+
+ EXPECT_TRUE(ReadFileToString(file_path, NULL));
+
+ data = "temp";
+ EXPECT_FALSE(ReadFileToString(file_path_dangerous, &data));
+ EXPECT_EQ(0u, data.length());
+
+ // Delete test file.
+ EXPECT_TRUE(base::DeleteFile(file_path, false));
+
+ data = "temp";
+ EXPECT_FALSE(ReadFileToString(file_path, &data));
+ EXPECT_EQ(0u, data.length());
+
+ data = "temp";
+ EXPECT_FALSE(ReadFileToString(file_path, &data, 6));
+ EXPECT_EQ(0u, data.length());
+}
+
TEST_F(FileUtilTest, TouchFile) {
FilePath data_dir =
temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
@@ -1897,13 +2100,13 @@ TEST_F(FileUtilTest, TouchFile) {
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()));
+ ASSERT_TRUE(WriteFile(foobar, data.c_str(), data.length()));
Time access_time;
// This timestamp is divisible by one day (in local timezone),
// to make it work on FAT too.
ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00",
- &access_time));
+ &access_time));
Time modification_time;
// Note that this timestamp is divisible by two (seconds) - FAT stores
@@ -1912,12 +2115,12 @@ TEST_F(FileUtilTest, TouchFile) {
&modification_time));
ASSERT_TRUE(TouchFile(foobar, access_time, modification_time));
- PlatformFileInfo file_info;
+ File::Info file_info;
ASSERT_TRUE(GetFileInfo(foobar, &file_info));
- EXPECT_EQ(file_info.last_accessed.ToInternalValue(),
- access_time.ToInternalValue());
- EXPECT_EQ(file_info.last_modified.ToInternalValue(),
- modification_time.ToInternalValue());
+ EXPECT_EQ(access_time.ToInternalValue(),
+ file_info.last_accessed.ToInternalValue());
+ EXPECT_EQ(modification_time.ToInternalValue(),
+ file_info.last_modified.ToInternalValue());
}
TEST_F(FileUtilTest, IsDirectoryEmpty) {
@@ -1931,7 +2134,7 @@ TEST_F(FileUtilTest, IsDirectoryEmpty) {
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()));
+ ASSERT_TRUE(WriteFile(foo, bar.c_str(), bar.length()));
EXPECT_FALSE(IsDirectoryEmpty(empty_dir));
}
@@ -2008,23 +2211,23 @@ TEST_F(VerifyPathControlledByUserTest, BadPaths) {
.AppendASCII("not")
.AppendASCII("exist");
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, does_not_exist, uid_, ok_gids_));
// |base| not a subpath of |path|.
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::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(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
}
@@ -2037,10 +2240,10 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) {
<< "Failed to create symlink.";
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, file_link, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
file_link, file_link, uid_, ok_gids_));
// Symlink from one directory to another within the path.
@@ -2052,16 +2255,16 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) {
ASSERT_TRUE(PathExists(file_path_with_link));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, file_path_with_link, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::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(
+ base::VerifyPathControlledByUser(
file_path_with_link, file_path_with_link, uid_, ok_gids_));
}
@@ -2079,35 +2282,35 @@ TEST_F(VerifyPathControlledByUserTest, OwnershipChecks) {
// We control these paths.
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, ok_gids_));
// Another user does not control these paths.
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, bad_uid, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, bad_uid, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, bad_uid, ok_gids_));
// Another group does not control the paths.
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, bad_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, bad_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, bad_gids_));
}
@@ -2122,36 +2325,36 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) {
// Any group is okay because the path is not group-writable.
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, bad_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, bad_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, no_gids));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, no_gids));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, no_gids));
@@ -2165,23 +2368,23 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) {
// Now |ok_gids_| works, but |bad_gids_| fails.
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, bad_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, bad_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, bad_gids_));
// Because any group in the group set is allowed,
@@ -2194,13 +2397,13 @@ TEST_F(VerifyPathControlledByUserTest, GroupWriteTest) {
std::inserter(multiple_gids, multiple_gids.begin()));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, multiple_gids));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, multiple_gids));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, multiple_gids));
}
@@ -2215,78 +2418,78 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
// Initialy, we control all parts of the path.
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::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::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_FALSE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, ok_gids_));
// Back to the initial state: Nothing is writable, so every path
@@ -2294,13 +2497,13 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
ASSERT_NO_FATAL_FAILURE(
ChangePosixFilePermissions(text_file_, 0u, S_IWOTH));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, sub_dir_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
base_dir_, text_file_, uid_, ok_gids_));
EXPECT_TRUE(
- file_util::VerifyPathControlledByUser(
+ base::VerifyPathControlledByUser(
sub_dir_, text_file_, uid_, ok_gids_));
}
@@ -2329,9 +2532,9 @@ TEST_F(FileUtilTest, ValidContentUriTest) {
// We should be able to read the file.
char* buffer = new char[image_size];
- int fd = OpenContentUriForRead(path);
- EXPECT_LT(0, fd);
- EXPECT_TRUE(ReadFromFD(fd, buffer, image_size));
+ File file = OpenContentUriForRead(path);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.ReadAtCurrentPos(buffer, image_size));
delete[] buffer;
}
@@ -2344,11 +2547,48 @@ TEST_F(FileUtilTest, NonExistentContentUriTest) {
EXPECT_FALSE(GetFileSize(path, &size));
// We should not be able to read the file.
- int fd = OpenContentUriForRead(path);
- EXPECT_EQ(-1, fd);
+ File file = OpenContentUriForRead(path);
+ EXPECT_FALSE(file.IsValid());
}
#endif
+TEST(ScopedFD, ScopedFDDoesClose) {
+ int fds[2];
+ char c = 0;
+ ASSERT_EQ(0, pipe(fds));
+ const int write_end = fds[1];
+ base::ScopedFD read_end_closer(fds[0]);
+ {
+ base::ScopedFD write_end_closer(fds[1]);
+ }
+ // This is the only thread. This file descriptor should no longer be valid.
+ int ret = close(write_end);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EBADF, errno);
+ // Make sure read(2) won't block.
+ ASSERT_EQ(0, fcntl(fds[0], F_SETFL, O_NONBLOCK));
+ // Reading the pipe should EOF.
+ EXPECT_EQ(0, read(fds[0], &c, 1));
+}
+
+#if defined(GTEST_HAS_DEATH_TEST)
+void CloseWithScopedFD(int fd) {
+ base::ScopedFD fd_closer(fd);
+}
+#endif
+
+TEST(ScopedFD, ScopedFDCrashesOnCloseFailure) {
+ int fds[2];
+ ASSERT_EQ(0, pipe(fds));
+ base::ScopedFD read_end_closer(fds[0]);
+ EXPECT_EQ(0, IGNORE_EINTR(close(fds[1])));
+#if defined(GTEST_HAS_DEATH_TEST)
+ // This is the only thread. This file descriptor should no longer be valid.
+ // Trying to close it should crash. This is important for security.
+ EXPECT_DEATH(CloseWithScopedFD(fds[1]), "");
+#endif
+}
+
#endif // defined(OS_POSIX)
} // namespace
diff --git a/chromium/base/file_util_win.cc b/chromium/base/file_util_win.cc
index 44c1205aa65..e3cd1f83ac2 100644
--- a/chromium/base/file_util_win.cc
+++ b/chromium/base/file_util_win.cc
@@ -5,6 +5,7 @@
#include "base/file_util.h"
#include <windows.h>
+#include <io.h>
#include <psapi.h>
#include <shellapi.h>
#include <shlobj.h>
@@ -14,6 +15,7 @@
#include <limits>
#include <string>
+#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
@@ -34,44 +36,6 @@ namespace {
const DWORD kFileShareAll =
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
-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();
-
- 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);
-}
-
} // namespace
FilePath MakeAbsoluteFilePath(const FilePath& input) {
@@ -88,10 +52,15 @@ bool DeleteFile(const FilePath& path, bool recursive) {
if (path.value().length() >= MAX_PATH)
return false;
+ // On XP SHFileOperation will return ERROR_ACCESS_DENIED instead of
+ // ERROR_FILE_NOT_FOUND, so just shortcut this here.
+ if (path.empty())
+ return true;
+
if (!recursive) {
// If not recursing, then first check to see if |path| is a directory.
// If it is, then remove it with RemoveDirectory.
- PlatformFileInfo file_info;
+ File::Info file_info;
if (GetFileInfo(path, &file_info) && file_info.is_directory)
return RemoveDirectory(path.value().c_str()) != 0;
@@ -126,8 +95,10 @@ bool DeleteFile(const FilePath& path, bool recursive) {
// 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);
+ // ERROR_FILE_NOT_FOUND. MSDN says Vista and up won't return 0x402. Windows 7
+ // can return DE_INVALIDFILES (0x7C) for nonexistent directories.
+ return (err == 0 || err == ERROR_FILE_NOT_FOUND || err == 0x402 ||
+ err == 0x7C);
}
bool DeleteFileAfterReboot(const FilePath& path) {
@@ -143,7 +114,7 @@ bool DeleteFileAfterReboot(const FilePath& path) {
bool ReplaceFile(const FilePath& from_path,
const FilePath& to_path,
- PlatformFileError* error) {
+ File::Error* error) {
ThreadRestrictions::AssertIOAllowed();
// Try a simple move first. It will only succeed when |to_path| doesn't
// already exist.
@@ -158,33 +129,99 @@ bool ReplaceFile(const FilePath& from_path,
return true;
}
if (error)
- *error = LastErrorToPlatformFileError(GetLastError());
+ *error = File::OSErrorToFileError(GetLastError());
return false;
}
bool CopyDirectory(const FilePath& from_path, const FilePath& to_path,
bool recursive) {
+ // NOTE(maruel): Previous version of this function used to call
+ // SHFileOperation(). This used to copy the file attributes and extended
+ // attributes, OLE structured storage, NTFS file system alternate data
+ // streams, SECURITY_DESCRIPTOR. In practice, this is not what we want, we
+ // want the containing directory to propagate its SECURITY_DESCRIPTOR.
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;
+ }
+
+ // This function does not properly handle destinations within the source.
+ FilePath real_to_path = to_path;
+ if (PathExists(real_to_path)) {
+ real_to_path = MakeAbsoluteFilePath(real_to_path);
+ if (real_to_path.empty())
+ return false;
+ } else {
+ real_to_path = MakeAbsoluteFilePath(real_to_path.DirName());
+ if (real_to_path.empty())
+ return false;
+ }
+ FilePath real_from_path = MakeAbsoluteFilePath(from_path);
+ if (real_from_path.empty())
+ 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;
+ }
+
+ int traverse_type = FileEnumerator::FILES;
if (recursive)
- return ShellCopy(from_path, to_path, true);
+ traverse_type |= FileEnumerator::DIRECTORIES;
+ FileEnumerator traversal(from_path, recursive, traverse_type);
+
+ if (!PathExists(from_path)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't stat source directory: "
+ << from_path.value().c_str();
+ return false;
+ }
+ // TODO(maruel): This is not necessary anymore.
+ DCHECK(recursive || DirectoryExists(from_path));
+
+ FilePath current = from_path;
+ bool from_is_dir = DirectoryExists(from_path);
+ bool success = true;
+ FilePath from_path_base = from_path;
+ if (recursive && DirectoryExists(to_path)) {
+ // 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 following code assumes that from path is a directory.
- DCHECK(DirectoryExists(from_path));
+ 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 (from_is_dir) {
+ if (!DirectoryExists(target_path) &&
+ !::CreateDirectory(target_path.value().c_str(), NULL)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create directory: "
+ << target_path.value().c_str();
+ success = false;
+ }
+ } else if (!internal::CopyFileUnsafe(current, target_path)) {
+ DLOG(ERROR) << "CopyDirectory() couldn't create file: "
+ << target_path.value().c_str();
+ success = false;
+ }
- // 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);
+ current = traversal.Next();
+ if (!current.empty())
+ from_is_dir = traversal.GetInfo().IsDirectory();
}
- FilePath directory = from_path.Append(L"*.*");
- return ShellCopy(directory, to_path, false);
+ return success;
}
bool PathExists(const FilePath& path) {
@@ -225,8 +262,21 @@ bool GetTempDir(FilePath* path) {
return true;
}
-bool GetShmemTempDir(bool executable, FilePath* path) {
- return GetTempDir(path);
+FilePath GetHomeDir() {
+ char16 result[MAX_PATH];
+ if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT,
+ result)) &&
+ result[0]) {
+ return FilePath(result);
+ }
+
+ // Fall back to the temporary directory on failure.
+ FilePath temp;
+ if (GetTempDir(&temp))
+ return temp;
+
+ // Last resort.
+ return FilePath(L"C:\\");
}
bool CreateTemporaryFile(FilePath* path) {
@@ -245,11 +295,6 @@ bool CreateTemporaryFile(FilePath* path) {
return false;
}
-FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
- 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
@@ -328,7 +373,7 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix,
}
bool CreateDirectoryAndGetError(const FilePath& full_path,
- PlatformFileError* error) {
+ File::Error* error) {
ThreadRestrictions::AssertIOAllowed();
// If the path exists, we've succeeded if it's a directory, failed otherwise.
@@ -343,7 +388,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
<< "conflicts with existing file.";
if (error) {
- *error = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
+ *error = File::FILE_ERROR_NOT_A_DIRECTORY;
}
return false;
}
@@ -356,14 +401,14 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
FilePath parent_path(full_path.DirName());
if (parent_path.value() == full_path.value()) {
if (error) {
- *error = PLATFORM_FILE_ERROR_NOT_FOUND;
+ *error = File::FILE_ERROR_NOT_FOUND;
}
return false;
}
if (!CreateDirectoryAndGetError(parent_path, error)) {
DLOG(WARNING) << "Failed to create one of the parent directories.";
if (error) {
- DCHECK(*error != PLATFORM_FILE_OK);
+ DCHECK(*error != File::FILE_OK);
}
return false;
}
@@ -378,7 +423,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
return true;
} else {
if (error)
- *error = LastErrorToPlatformFileError(error_code);
+ *error = File::OSErrorToFileError(error_code);
DLOG(WARNING) << "Failed to create directory " << full_path_str
<< ", last error is " << error_code << ".";
return false;
@@ -505,7 +550,7 @@ bool IsLink(const FilePath& file_path) {
return false;
}
-bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) {
+bool GetFileInfo(const FilePath& file_path, File::Info* results) {
ThreadRestrictions::AssertIOAllowed();
WIN32_FILE_ATTRIBUTE_DATA attr;
@@ -534,7 +579,21 @@ FILE* OpenFile(const FilePath& filename, const char* mode) {
return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
}
-int ReadFile(const FilePath& filename, char* data, int size) {
+FILE* FileToFILE(File file, const char* mode) {
+ if (!file.IsValid())
+ return NULL;
+ int fd =
+ _open_osfhandle(reinterpret_cast<intptr_t>(file.GetPlatformFile()), 0);
+ if (fd < 0)
+ return NULL;
+ file.TakePlatformFile();
+ FILE* stream = _fdopen(fd, mode);
+ if (!stream)
+ _close(fd);
+ return stream;
+}
+
+int ReadFile(const FilePath& filename, char* data, int max_size) {
ThreadRestrictions::AssertIOAllowed();
base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
GENERIC_READ,
@@ -547,29 +606,14 @@ int ReadFile(const FilePath& filename, char* data, int size) {
return -1;
DWORD read;
- if (::ReadFile(file, data, size, &read, NULL) &&
- static_cast<int>(read) == size)
+ if (::ReadFile(file, data, max_size, &read, NULL))
return read;
- return -1;
-}
-
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::DirectoryExists;
-using base::FilePath;
-using base::kFileShareAll;
-FILE* OpenFile(const std::string& filename, const char* mode) {
- base::ThreadRestrictions::AssertIOAllowed();
- return _fsopen(filename.c_str(), mode, _SH_DENYNO);
+ return -1;
}
int WriteFile(const FilePath& filename, const char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
GENERIC_WRITE,
0,
@@ -578,8 +622,8 @@ int WriteFile(const FilePath& filename, const char* data, int size) {
0,
NULL));
if (!file) {
- DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path "
- << filename.value();
+ DPLOG(WARNING) << "CreateFile failed for path "
+ << UTF16ToUTF8(filename.value());
return -1;
}
@@ -590,18 +634,18 @@ int WriteFile(const FilePath& filename, const char* data, int size) {
if (!result) {
// WriteFile failed.
- DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value()
- << " failed";
+ DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value())
+ << " failed";
} else {
// Didn't write all the bytes.
DLOG(WARNING) << "wrote" << written << " bytes to "
- << filename.value() << " expected " << size;
+ << UTF16ToUTF8(filename.value()) << " expected " << size;
}
return -1;
}
int AppendToFile(const FilePath& filename, const char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
FILE_APPEND_DATA,
0,
@@ -610,8 +654,8 @@ int AppendToFile(const FilePath& filename, const char* data, int size) {
0,
NULL));
if (!file) {
- DLOG_GETLASTERROR(WARNING) << "CreateFile failed for path "
- << filename.value();
+ DPLOG(WARNING) << "CreateFile failed for path "
+ << UTF16ToUTF8(filename.value());
return -1;
}
@@ -622,19 +666,19 @@ int AppendToFile(const FilePath& filename, const char* data, int size) {
if (!result) {
// WriteFile failed.
- DLOG_GETLASTERROR(WARNING) << "writing file " << filename.value()
- << " failed";
+ DPLOG(WARNING) << "writing file " << UTF16ToUTF8(filename.value())
+ << " failed";
} else {
// Didn't write all the bytes.
DLOG(WARNING) << "wrote" << written << " bytes to "
- << filename.value() << " expected " << size;
+ << UTF16ToUTF8(filename.value()) << " expected " << size;
}
return -1;
}
// Gets the current working directory for the process.
bool GetCurrentDirectory(FilePath* dir) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
wchar_t system_buffer[MAX_PATH];
system_buffer[0] = 0;
@@ -651,13 +695,13 @@ bool GetCurrentDirectory(FilePath* dir) {
// Sets the current working directory for the process.
bool SetCurrentDirectory(const FilePath& directory) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
BOOL ret = ::SetCurrentDirectory(directory.value().c_str());
return ret != 0;
}
int GetMaximumPathComponentLength(const FilePath& path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
wchar_t volume_path[MAX_PATH];
if (!GetVolumePathNameW(path.NormalizePathSeparators().value().c_str(),
@@ -680,9 +724,8 @@ int GetMaximumPathComponentLength(const FilePath& path) {
return std::min(whole_path_limit, static_cast<int>(max_length));
}
-} // namespace file_util
+// -----------------------------------------------------------------------------
-namespace base {
namespace internal {
bool MoveUnsafe(const FilePath& from_path, const FilePath& to_path) {
@@ -728,8 +771,24 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
to_path.value().length() >= MAX_PATH) {
return false;
}
- return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(),
- false) != 0);
+
+ // Unlike the posix implementation that copies the file manually and discards
+ // the ACL bits, CopyFile() copies the complete SECURITY_DESCRIPTOR and access
+ // bits, which is usually not what we want. We can't do much about the
+ // SECURITY_DESCRIPTOR but at least remove the read only bit.
+ const wchar_t* dest = to_path.value().c_str();
+ if (!::CopyFile(from_path.value().c_str(), dest, false)) {
+ // Copy failed.
+ return false;
+ }
+ DWORD attrs = GetFileAttributes(dest);
+ if (attrs == INVALID_FILE_ATTRIBUTES) {
+ return false;
+ }
+ if (attrs & FILE_ATTRIBUTE_READONLY) {
+ SetFileAttributes(dest, attrs & ~FILE_ATTRIBUTE_READONLY);
+ }
+ return true;
}
bool CopyAndDeleteDirectory(const FilePath& from_path,
diff --git a/chromium/base/file_version_info_mac.h b/chromium/base/file_version_info_mac.h
index f488cce4e58..f0edb3aea9f 100644
--- a/chromium/base/file_version_info_mac.h
+++ b/chromium/base/file_version_info_mac.h
@@ -23,27 +23,27 @@ class FileVersionInfoMac : public FileVersionInfo {
// 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 base::string16 company_name() OVERRIDE;
+ virtual base::string16 company_short_name() OVERRIDE;
+ virtual base::string16 product_name() OVERRIDE;
+ virtual base::string16 product_short_name() OVERRIDE;
+ virtual base::string16 internal_name() OVERRIDE;
+ virtual base::string16 product_version() OVERRIDE;
+ virtual base::string16 private_build() OVERRIDE;
+ virtual base::string16 special_build() OVERRIDE;
+ virtual base::string16 comments() OVERRIDE;
+ virtual base::string16 original_filename() OVERRIDE;
+ virtual base::string16 file_description() OVERRIDE;
+ virtual base::string16 file_version() OVERRIDE;
+ virtual base::string16 legal_copyright() OVERRIDE;
+ virtual base::string16 legal_trademarks() OVERRIDE;
+ virtual base::string16 last_change() OVERRIDE;
virtual bool is_official_build() OVERRIDE;
private:
- // Returns a string16 value for a property name.
+ // Returns a base::string16 value for a property name.
// Returns the empty string if the property does not exist.
- string16 GetString16Value(CFStringRef name);
+ base::string16 GetString16Value(CFStringRef name);
base::scoped_nsobject<NSBundle> bundle_;
diff --git a/chromium/base/file_version_info_mac.mm b/chromium/base/file_version_info_mac.mm
index 0c6f8d4d3ca..3b5a8ba9bf8 100644
--- a/chromium/base/file_version_info_mac.mm
+++ b/chromium/base/file_version_info_mac.mm
@@ -31,35 +31,35 @@ FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
return new FileVersionInfoMac(bundle);
}
-string16 FileVersionInfoMac::company_name() {
- return string16();
+base::string16 FileVersionInfoMac::company_name() {
+ return base::string16();
}
-string16 FileVersionInfoMac::company_short_name() {
- return string16();
+base::string16 FileVersionInfoMac::company_short_name() {
+ return base::string16();
}
-string16 FileVersionInfoMac::internal_name() {
- return string16();
+base::string16 FileVersionInfoMac::internal_name() {
+ return base::string16();
}
-string16 FileVersionInfoMac::product_name() {
+base::string16 FileVersionInfoMac::product_name() {
return GetString16Value(kCFBundleNameKey);
}
-string16 FileVersionInfoMac::product_short_name() {
+base::string16 FileVersionInfoMac::product_short_name() {
return GetString16Value(kCFBundleNameKey);
}
-string16 FileVersionInfoMac::comments() {
- return string16();
+base::string16 FileVersionInfoMac::comments() {
+ return base::string16();
}
-string16 FileVersionInfoMac::legal_copyright() {
+base::string16 FileVersionInfoMac::legal_copyright() {
return GetString16Value(CFSTR("CFBundleGetInfoString"));
}
-string16 FileVersionInfoMac::product_version() {
+base::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
@@ -72,31 +72,31 @@ string16 FileVersionInfoMac::product_version() {
#endif // defined(OS_IOS)
}
-string16 FileVersionInfoMac::file_description() {
- return string16();
+base::string16 FileVersionInfoMac::file_description() {
+ return base::string16();
}
-string16 FileVersionInfoMac::legal_trademarks() {
- return string16();
+base::string16 FileVersionInfoMac::legal_trademarks() {
+ return base::string16();
}
-string16 FileVersionInfoMac::private_build() {
- return string16();
+base::string16 FileVersionInfoMac::private_build() {
+ return base::string16();
}
-string16 FileVersionInfoMac::file_version() {
+base::string16 FileVersionInfoMac::file_version() {
return product_version();
}
-string16 FileVersionInfoMac::original_filename() {
+base::string16 FileVersionInfoMac::original_filename() {
return GetString16Value(kCFBundleNameKey);
}
-string16 FileVersionInfoMac::special_build() {
- return string16();
+base::string16 FileVersionInfoMac::special_build() {
+ return base::string16();
}
-string16 FileVersionInfoMac::last_change() {
+base::string16 FileVersionInfoMac::last_change() {
return GetString16Value(CFSTR("SCMRevision"));
}
@@ -108,7 +108,7 @@ bool FileVersionInfoMac::is_official_build() {
#endif
}
-string16 FileVersionInfoMac::GetString16Value(CFStringRef name) {
+base::string16 FileVersionInfoMac::GetString16Value(CFStringRef name) {
if (bundle_) {
NSString *ns_name = base::mac::CFToNSCast(name);
NSString* value = [bundle_ objectForInfoDictionaryKey:ns_name];
@@ -116,5 +116,5 @@ string16 FileVersionInfoMac::GetString16Value(CFStringRef name) {
return base::SysNSStringToUTF16(value);
}
}
- return string16();
+ return base::string16();
}
diff --git a/chromium/base/file_version_info_win.cc b/chromium/base/file_version_info_win.cc
index 80dbaeaadbf..5f33d1470f1 100644
--- a/chromium/base/file_version_info_win.cc
+++ b/chromium/base/file_version_info_win.cc
@@ -81,63 +81,63 @@ FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
}
}
-string16 FileVersionInfoWin::company_name() {
+base::string16 FileVersionInfoWin::company_name() {
return GetStringValue(L"CompanyName");
}
-string16 FileVersionInfoWin::company_short_name() {
+base::string16 FileVersionInfoWin::company_short_name() {
return GetStringValue(L"CompanyShortName");
}
-string16 FileVersionInfoWin::internal_name() {
+base::string16 FileVersionInfoWin::internal_name() {
return GetStringValue(L"InternalName");
}
-string16 FileVersionInfoWin::product_name() {
+base::string16 FileVersionInfoWin::product_name() {
return GetStringValue(L"ProductName");
}
-string16 FileVersionInfoWin::product_short_name() {
+base::string16 FileVersionInfoWin::product_short_name() {
return GetStringValue(L"ProductShortName");
}
-string16 FileVersionInfoWin::comments() {
+base::string16 FileVersionInfoWin::comments() {
return GetStringValue(L"Comments");
}
-string16 FileVersionInfoWin::legal_copyright() {
+base::string16 FileVersionInfoWin::legal_copyright() {
return GetStringValue(L"LegalCopyright");
}
-string16 FileVersionInfoWin::product_version() {
+base::string16 FileVersionInfoWin::product_version() {
return GetStringValue(L"ProductVersion");
}
-string16 FileVersionInfoWin::file_description() {
+base::string16 FileVersionInfoWin::file_description() {
return GetStringValue(L"FileDescription");
}
-string16 FileVersionInfoWin::legal_trademarks() {
+base::string16 FileVersionInfoWin::legal_trademarks() {
return GetStringValue(L"LegalTrademarks");
}
-string16 FileVersionInfoWin::private_build() {
+base::string16 FileVersionInfoWin::private_build() {
return GetStringValue(L"PrivateBuild");
}
-string16 FileVersionInfoWin::file_version() {
+base::string16 FileVersionInfoWin::file_version() {
return GetStringValue(L"FileVersion");
}
-string16 FileVersionInfoWin::original_filename() {
+base::string16 FileVersionInfoWin::original_filename() {
return GetStringValue(L"OriginalFilename");
}
-string16 FileVersionInfoWin::special_build() {
+base::string16 FileVersionInfoWin::special_build() {
return GetStringValue(L"SpecialBuild");
}
-string16 FileVersionInfoWin::last_change() {
+base::string16 FileVersionInfoWin::last_change() {
return GetStringValue(L"LastChange");
}
diff --git a/chromium/base/file_version_info_win.h b/chromium/base/file_version_info_win.h
index a37857783a9..d623c454261 100644
--- a/chromium/base/file_version_info_win.h
+++ b/chromium/base/file_version_info_win.h
@@ -22,21 +22,21 @@ class FileVersionInfoWin : public FileVersionInfo {
// 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 base::string16 company_name() OVERRIDE;
+ virtual base::string16 company_short_name() OVERRIDE;
+ virtual base::string16 product_name() OVERRIDE;
+ virtual base::string16 product_short_name() OVERRIDE;
+ virtual base::string16 internal_name() OVERRIDE;
+ virtual base::string16 product_version() OVERRIDE;
+ virtual base::string16 private_build() OVERRIDE;
+ virtual base::string16 special_build() OVERRIDE;
+ virtual base::string16 comments() OVERRIDE;
+ virtual base::string16 original_filename() OVERRIDE;
+ virtual base::string16 file_description() OVERRIDE;
+ virtual base::string16 file_version() OVERRIDE;
+ virtual base::string16 legal_copyright() OVERRIDE;
+ virtual base::string16 legal_trademarks() OVERRIDE;
+ virtual base::string16 last_change() OVERRIDE;
virtual bool is_official_build() OVERRIDE;
// Lets you access other properties not covered above.
@@ -50,7 +50,7 @@ class FileVersionInfoWin : public FileVersionInfo {
VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; }
private:
- scoped_ptr_malloc<char> data_;
+ scoped_ptr<char, base::FreeDeleter> data_;
int language_;
int code_page_;
// This is a pointer into the data_ if it exists. Otherwise NULL.
diff --git a/chromium/base/files/OWNERS b/chromium/base/files/OWNERS
index 7260260bb41..b99e8a2fc7a 100644
--- a/chromium/base/files/OWNERS
+++ b/chromium/base/files/OWNERS
@@ -1,2 +1,3 @@
-# for file_path_watcher*
-mnissler@chromium.org
+rvargas@chromium.org
+
+per-file file_path_watcher*=mnissler@chromium.org
diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc
index 4902f15a2ff..bfe87bdcc4d 100644
--- a/chromium/base/files/file.cc
+++ b/chromium/base/files/file.cc
@@ -20,7 +20,7 @@ File::Info::~Info() {
File::File()
: file_(kInvalidPlatformFileValue),
- error_(FILE_OK),
+ error_details_(FILE_ERROR_FAILED),
created_(false),
async_(false) {
}
@@ -28,25 +28,39 @@ File::File()
#if !defined(OS_NACL)
File::File(const FilePath& name, uint32 flags)
: file_(kInvalidPlatformFileValue),
- error_(FILE_OK),
+ error_details_(FILE_OK),
created_(false),
async_(false) {
- if (name.ReferencesParent()) {
- error_ = FILE_ERROR_ACCESS_DENIED;
- return;
- }
- CreateBaseFileUnsafe(name, flags);
+ Initialize(name, flags);
}
#endif
+File::File(PlatformFile platform_file)
+ : file_(platform_file),
+ error_details_(FILE_OK),
+ created_(false),
+ async_(false) {
+#if defined(OS_POSIX)
+ DCHECK_GE(platform_file, -1);
+#endif
+}
+
+File::File(Error error_details)
+ : file_(kInvalidPlatformFileValue),
+ error_details_(error_details),
+ created_(false),
+ async_(false) {
+}
+
File::File(RValue other)
: file_(other.object->TakePlatformFile()),
- error_(other.object->error()),
+ error_details_(other.object->error_details()),
created_(other.object->created()),
async_(other.object->async_) {
}
File::~File() {
+ // Go through the AssertIOAllowed logic.
Close();
}
@@ -54,11 +68,65 @@ File& File::operator=(RValue other) {
if (this != other.object) {
Close();
SetPlatformFile(other.object->TakePlatformFile());
- error_ = other.object->error();
+ error_details_ = other.object->error_details();
created_ = other.object->created();
async_ = other.object->async_;
}
return *this;
}
+#if !defined(OS_NACL)
+void File::Initialize(const FilePath& name, uint32 flags) {
+ if (name.ReferencesParent()) {
+ error_details_ = FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+ InitializeUnsafe(name, flags);
+}
+#endif
+
+std::string File::ErrorToString(Error error) {
+ switch (error) {
+ case FILE_OK:
+ return "FILE_OK";
+ case FILE_ERROR_FAILED:
+ return "FILE_ERROR_FAILED";
+ case FILE_ERROR_IN_USE:
+ return "FILE_ERROR_IN_USE";
+ case FILE_ERROR_EXISTS:
+ return "FILE_ERROR_EXISTS";
+ case FILE_ERROR_NOT_FOUND:
+ return "FILE_ERROR_NOT_FOUND";
+ case FILE_ERROR_ACCESS_DENIED:
+ return "FILE_ERROR_ACCESS_DENIED";
+ case FILE_ERROR_TOO_MANY_OPENED:
+ return "FILE_ERROR_TOO_MANY_OPENED";
+ case FILE_ERROR_NO_MEMORY:
+ return "FILE_ERROR_NO_MEMORY";
+ case FILE_ERROR_NO_SPACE:
+ return "FILE_ERROR_NO_SPACE";
+ case FILE_ERROR_NOT_A_DIRECTORY:
+ return "FILE_ERROR_NOT_A_DIRECTORY";
+ case FILE_ERROR_INVALID_OPERATION:
+ return "FILE_ERROR_INVALID_OPERATION";
+ case FILE_ERROR_SECURITY:
+ return "FILE_ERROR_SECURITY";
+ case FILE_ERROR_ABORT:
+ return "FILE_ERROR_ABORT";
+ case FILE_ERROR_NOT_A_FILE:
+ return "FILE_ERROR_NOT_A_FILE";
+ case FILE_ERROR_NOT_EMPTY:
+ return "FILE_ERROR_NOT_EMPTY";
+ case FILE_ERROR_INVALID_URL:
+ return "FILE_ERROR_INVALID_URL";
+ case FILE_ERROR_IO:
+ return "FILE_ERROR_IO";
+ case FILE_ERROR_MAX:
+ break;
+ }
+
+ NOTREACHED();
+ return "";
+}
+
} // namespace base
diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h
index d1e0e8ca587..1913bc7f6c4 100644
--- a/chromium/base/files/file.h
+++ b/chromium/base/files/file.h
@@ -10,11 +10,15 @@
#include <windows.h>
#endif
+#if defined(OS_POSIX)
+#include <sys/stat.h>
+#endif
+
#include <string>
#include "base/base_export.h"
#include "base/basictypes.h"
-#include "base/files/file_path.h"
+#include "base/files/scoped_file.h"
#include "base/move.h"
#include "base/time/time.h"
@@ -24,12 +28,19 @@
namespace base {
+class FilePath;
+
#if defined(OS_WIN)
typedef HANDLE PlatformFile;
#elif defined(OS_POSIX)
typedef int PlatformFile;
-#endif
+#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
+typedef struct stat stat_wrapper_t;
+#else
+typedef struct stat64 stat_wrapper_t;
+#endif
+#endif // defined(OS_POSIX)
// Thin wrapper around an OS-level file.
// Note that this class does not provide any support for asynchronous IO, other
@@ -120,6 +131,10 @@ class BASE_EXPORT File {
struct BASE_EXPORT Info {
Info();
~Info();
+#if defined(OS_POSIX)
+ // Fills this struct with values from |stat_info|.
+ void FromStat(const stat_wrapper_t& stat_info);
+#endif
// The size of the file in bytes. Undefined when is_directory is true.
int64 size;
@@ -149,6 +164,9 @@ class BASE_EXPORT File {
// Takes ownership of |platform_file|.
explicit File(PlatformFile platform_file);
+ // Creates an object with a specific error_details code.
+ explicit File(Error error_details);
+
// Move constructor for C++03 move emulation of this type.
File(RValue other);
@@ -157,9 +175,12 @@ class BASE_EXPORT File {
// Move operator= for C++03 move emulation of this type.
File& operator=(RValue other);
+ // Creates or opens the given file.
+ void Initialize(const FilePath& name, uint32 flags);
+
// Creates or opens the given file, allowing paths with traversal ('..')
// components. Use only with extreme care.
- void CreateBaseFileUnsafe(const FilePath& name, uint32 flags);
+ void InitializeUnsafe(const FilePath& name, uint32 flags);
bool IsValid() const;
@@ -168,10 +189,14 @@ class BASE_EXPORT File {
// FLAG_CREATE_ALWAYS), and false otherwise.
bool created() const { return created_; }
- // Returns the OS result of opening this file.
- Error error() const { return error_; }
+ // Returns the OS result of opening this file. Note that the way to verify
+ // the success of the operation is to use IsValid(), not this method:
+ // File file(name, flags);
+ // if (!file.IsValid())
+ // return;
+ Error error_details() const { return error_details_; }
- PlatformFile GetPlatformFile() const { return file_; }
+ PlatformFile GetPlatformFile() const;
PlatformFile TakePlatformFile();
// Destroying this object closes the file automatically.
@@ -216,10 +241,13 @@ class BASE_EXPORT File {
// platforms. Returns the number of bytes written, or -1 on error.
int WriteAtCurrentPosNoBestEffort(const char* data, int size);
+ // Returns the current size of this file, or a negative number on failure.
+ int64 GetLength();
+
// Truncates the file to the given length. If |length| is greater than the
// current size of the file, the file is extended with zeros. If the file
// doesn't exist, |false| is returned.
- bool Truncate(int64 length);
+ bool SetLength(int64 length);
// Flushes the buffers.
bool Flush();
@@ -255,22 +283,27 @@ class BASE_EXPORT File {
// Unlock a file previously locked.
Error Unlock();
+ bool async() const { return async_; }
+
#if defined(OS_WIN)
static Error OSErrorToFileError(DWORD last_error);
#elif defined(OS_POSIX)
static Error OSErrorToFileError(int saved_errno);
#endif
+ // Converts an error value to a human-readable form. Used for logging.
+ static std::string ErrorToString(Error error);
+
private:
void SetPlatformFile(PlatformFile file);
#if defined(OS_WIN)
win::ScopedHandle file_;
#elif defined(OS_POSIX)
- PlatformFile file_;
+ ScopedFD file_;
#endif
- Error error_;
+ Error error_details_;
bool created_;
bool async_;
};
diff --git a/chromium/base/files/file_path.cc b/chromium/base/files/file_path.cc
index 3ea5856a0d5..a8b27139988 100644
--- a/chromium/base/files/file_path.cc
+++ b/chromium/base/files/file_path.cc
@@ -521,7 +521,7 @@ FilePath FilePath::Append(const FilePath& component) const {
}
FilePath FilePath::AppendASCII(const StringPiece& component) const {
- DCHECK(IsStringASCII(component));
+ DCHECK(base::IsStringASCII(component));
#if defined(OS_WIN)
return Append(ASCIIToUTF16(component.as_string()));
#elif defined(OS_POSIX)
@@ -587,7 +587,7 @@ string16 FilePath::LossyDisplayName() const {
}
std::string FilePath::MaybeAsASCII() const {
- if (IsStringASCII(path_))
+ if (base::IsStringASCII(path_))
return path_;
return std::string();
}
@@ -632,9 +632,9 @@ string16 FilePath::LossyDisplayName() const {
}
std::string FilePath::MaybeAsASCII() const {
- if (IsStringASCII(path_))
- return WideToASCII(path_);
- return "";
+ if (base::IsStringASCII(path_))
+ return UTF16ToASCII(path_);
+ return std::string();
}
std::string FilePath::AsUTF8Unsafe() const {
@@ -1292,10 +1292,16 @@ void FilePath::StripTrailingSeparatorsInternal() {
}
FilePath FilePath::NormalizePathSeparators() const {
+ return NormalizePathSeparatorsTo(kSeparators[0]);
+}
+
+FilePath FilePath::NormalizePathSeparatorsTo(CharType separator) const {
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
+ DCHECK_NE(kSeparators + kSeparatorsLength,
+ std::find(kSeparators, kSeparators + kSeparatorsLength, separator));
StringType copy = path_;
- for (size_t i = 1; i < kSeparatorsLength; ++i) {
- std::replace(copy.begin(), copy.end(), kSeparators[i], kSeparators[0]);
+ for (size_t i = 0; i < kSeparatorsLength; ++i) {
+ std::replace(copy.begin(), copy.end(), kSeparators[i], separator);
}
return FilePath(copy);
#else
diff --git a/chromium/base/files/file_path.h b/chromium/base/files/file_path.h
index f4b8ff8ade8..008b9f5afc6 100644
--- a/chromium/base/files/file_path.h
+++ b/chromium/base/files/file_path.h
@@ -189,6 +189,13 @@ class BASE_EXPORT FilePath {
// 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.
+ //
+ // To make sure this is lossless so we can differentiate absolute and
+ // relative paths, the root slash will be included even though no other
+ // slashes will be. The precise behavior is:
+ //
+ // Posix: "/foo/bar" -> [ "/", "foo", "bar" ]
+ // Windows: "C:\foo\bar" -> [ "C:", "\\", "foo", "bar" ]
void GetComponents(std::vector<FilePath::StringType>* components) const;
// Returns true if this FilePath is a strict parent of the |child|. Absolute
@@ -239,7 +246,7 @@ class BASE_EXPORT FilePath {
// TODO(davidben): Check all our extension-sensitive code to see if
// we can rename this to Extension() and the other to something like
// LongExtension(), defaulting to short extensions and leaving the
- // long "extensions" to logic like file_util::GetUniquePathNumber().
+ // long "extensions" to logic like base::GetUniquePathNumber().
StringType FinalExtension() const;
// Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
@@ -314,8 +321,8 @@ class BASE_EXPORT FilePath {
// 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 ".."
+ // Returns true if this FilePath contains an attempt to reference a parent
+ // directory (e.g. has a path component that is "..").
bool ReferencesParent() const;
// Return a Unicode human-readable version of this path.
@@ -367,6 +374,10 @@ class BASE_EXPORT FilePath {
// (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
FilePath NormalizePathSeparators() const;
+ // Normalize all path separattors to given type on Windows
+ // (if FILE_PATH_USES_WIN_SEPARATORS is true), or do nothing on POSIX systems.
+ FilePath NormalizePathSeparatorsTo(CharType separator) 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
diff --git a/chromium/base/files/file_path_watcher.cc b/chromium/base/files/file_path_watcher.cc
index 49e0a237f69..b17354197d4 100644
--- a/chromium/base/files/file_path_watcher.cc
+++ b/chromium/base/files/file_path_watcher.cc
@@ -10,6 +10,10 @@
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+#include "base/mac/mac_util.h"
+#endif
+
namespace base {
FilePathWatcher::~FilePathWatcher() {
@@ -22,6 +26,19 @@ void FilePathWatcher::CancelWatch(
delegate->CancelOnMessageLoopThread();
}
+// static
+bool FilePathWatcher::RecursiveWatchAvailable() {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ // FSEvents isn't available on iOS and is broken on OSX 10.6 and earlier.
+ // See http://crbug.com/54822#c31
+ return mac::IsOSLionOrLater();
+#elif defined(OS_WIN) || defined(OS_LINUX)
+ return true;
+#else
+ return false;
+#endif
+}
+
FilePathWatcher::PlatformDelegate::PlatformDelegate(): cancelled_(false) {
}
diff --git a/chromium/base/files/file_path_watcher.h b/chromium/base/files/file_path_watcher.h
index 3c1941f012b..b90efa1800f 100644
--- a/chromium/base/files/file_path_watcher.h
+++ b/chromium/base/files/file_path_watcher.h
@@ -88,12 +88,15 @@ class BASE_EXPORT FilePathWatcher {
// shutdown.
static void CancelWatch(const scoped_refptr<PlatformDelegate>& delegate);
+ // Returns true if the platform and OS version support recursive watches.
+ static bool RecursiveWatchAvailable();
+
// 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.
+ // 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);
diff --git a/chromium/base/files/file_path_watcher_browsertest.cc b/chromium/base/files/file_path_watcher_browsertest.cc
index aed409c7871..8f57cadda90 100644
--- a/chromium/base/files/file_path_watcher_browsertest.cc
+++ b/chromium/base/files/file_path_watcher_browsertest.cc
@@ -172,8 +172,7 @@ class FilePathWatcherTest : public testing::Test {
// 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());
+ int write_size = ::base::WriteFile(file, content.c_str(), content.length());
return write_size == static_cast<int>(content.length());
}
@@ -206,9 +205,8 @@ bool FilePathWatcherTest::SetupWatch(const FilePath& target,
bool result;
file_thread_.message_loop_proxy()->PostTask(
FROM_HERE,
- base::Bind(SetupWatchCallback,
- target, watcher, delegate, recursive_watch, &result,
- &completion));
+ base::Bind(SetupWatchCallback, target, watcher, delegate, recursive_watch,
+ &result, &completion));
completion.Wait();
return result;
}
@@ -484,12 +482,17 @@ TEST_F(FilePathWatcherTest, MoveParent) {
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));
+ bool setup_result = SetupWatch(dir, &watcher, delegate.get(), true);
+ if (!FilePathWatcher::RecursiveWatchAvailable()) {
+ ASSERT_FALSE(setup_result);
+ DeleteDelegateOnFileThread(delegate.release());
+ return;
+ }
+ ASSERT_TRUE(setup_result);
// Main directory("dir") creation.
ASSERT_TRUE(base::CreateDirectory(dir));
@@ -537,16 +540,48 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) {
ASSERT_TRUE(WaitForEvents());
DeleteDelegateOnFileThread(delegate.release());
}
-#else
-TEST_F(FilePathWatcherTest, RecursiveWatch) {
+
+#if defined(OS_POSIX)
+TEST_F(FilePathWatcherTest, RecursiveWithSymLink) {
+ if (!FilePathWatcher::RecursiveWatchAvailable())
+ return;
+
FilePathWatcher watcher;
- FilePath dir(temp_dir_.path().AppendASCII("dir"));
+ FilePath test_dir(temp_dir_.path().AppendASCII("test_dir"));
+ ASSERT_TRUE(base::CreateDirectory(test_dir));
+ FilePath symlink(test_dir.AppendASCII("symlink"));
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
- // Non-Windows implementaion does not support recursive watching.
- ASSERT_FALSE(SetupWatch(dir, &watcher, delegate.get(), true));
+ ASSERT_TRUE(SetupWatch(symlink, &watcher, delegate.get(), true));
+
+ // Link creation.
+ FilePath target1(temp_dir_.path().AppendASCII("target1"));
+ ASSERT_TRUE(base::CreateSymbolicLink(target1, symlink));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Target1 creation.
+ ASSERT_TRUE(base::CreateDirectory(target1));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create a file in target1.
+ FilePath target1_file(target1.AppendASCII("file"));
+ ASSERT_TRUE(WriteFile(target1_file, "content"));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Link change.
+ FilePath target2(temp_dir_.path().AppendASCII("target2"));
+ ASSERT_TRUE(base::CreateDirectory(target2));
+ ASSERT_TRUE(base::DeleteFile(symlink, false));
+ ASSERT_TRUE(base::CreateSymbolicLink(target2, symlink));
+ ASSERT_TRUE(WaitForEvents());
+
+ // Create a file in target2.
+ FilePath target2_file(target2.AppendASCII("file"));
+ ASSERT_TRUE(WriteFile(target2_file, "content"));
+ ASSERT_TRUE(WaitForEvents());
+
DeleteDelegateOnFileThread(delegate.release());
}
-#endif
+#endif // OS_POSIX
TEST_F(FilePathWatcherTest, MoveChild) {
FilePathWatcher file_watcher;
@@ -575,10 +610,6 @@ TEST_F(FilePathWatcherTest, MoveChild) {
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"));
@@ -592,8 +623,6 @@ TEST_F(FilePathWatcherTest, FileAttributesChanged) {
DeleteDelegateOnFileThread(delegate.release());
}
-#endif // !OS_LINUX
-
#if defined(OS_LINUX)
// Verify that creating a symlink is caught.
diff --git a/chromium/base/files/file_path_watcher_fsevents.cc b/chromium/base/files/file_path_watcher_fsevents.cc
new file mode 100644
index 00000000000..edf4d239465
--- /dev/null
+++ b/chromium/base/files/file_path_watcher_fsevents.cc
@@ -0,0 +1,263 @@
+// Copyright 2014 The Chromium Authors. All 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_fsevents.h"
+
+#include <list>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/mac/libdispatch_task_runner.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/message_loop/message_loop.h"
+
+namespace base {
+
+namespace {
+
+// The latency parameter passed to FSEventsStreamCreate().
+const CFAbsoluteTime kEventLatencySeconds = 0.3;
+
+class FSEventsTaskRunner : public mac::LibDispatchTaskRunner {
+ public:
+ FSEventsTaskRunner()
+ : mac::LibDispatchTaskRunner("org.chromium.FilePathWatcherFSEvents") {
+ }
+
+ protected:
+ virtual ~FSEventsTaskRunner() {}
+};
+
+static LazyInstance<FSEventsTaskRunner>::Leaky g_task_runner =
+ LAZY_INSTANCE_INITIALIZER;
+
+// Resolve any symlinks in the path.
+FilePath ResolvePath(const FilePath& path) {
+ const unsigned kMaxLinksToResolve = 255;
+
+ std::vector<FilePath::StringType> component_vector;
+ path.GetComponents(&component_vector);
+ std::list<FilePath::StringType>
+ components(component_vector.begin(), component_vector.end());
+
+ FilePath result;
+ unsigned resolve_count = 0;
+ while (resolve_count < kMaxLinksToResolve && !components.empty()) {
+ FilePath component(*components.begin());
+ components.pop_front();
+
+ FilePath current;
+ if (component.IsAbsolute()) {
+ current = component;
+ } else {
+ current = result.Append(component);
+ }
+
+ FilePath target;
+ if (ReadSymbolicLink(current, &target)) {
+ if (target.IsAbsolute())
+ result.clear();
+ std::vector<FilePath::StringType> target_components;
+ target.GetComponents(&target_components);
+ components.insert(components.begin(), target_components.begin(),
+ target_components.end());
+ resolve_count++;
+ } else {
+ result = current;
+ }
+ }
+
+ if (resolve_count >= kMaxLinksToResolve)
+ result.clear();
+ return result;
+}
+
+// The callback passed to FSEventStreamCreate().
+void FSEventsCallback(ConstFSEventStreamRef stream,
+ void* event_watcher, size_t num_events,
+ void* event_paths, const FSEventStreamEventFlags flags[],
+ const FSEventStreamEventId event_ids[]) {
+ FilePathWatcherFSEvents* watcher =
+ reinterpret_cast<FilePathWatcherFSEvents*>(event_watcher);
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+
+ bool root_changed = watcher->ResolveTargetPath();
+ std::vector<FilePath> paths;
+ FSEventStreamEventId root_change_at = FSEventStreamGetLatestEventId(stream);
+ for (size_t i = 0; i < num_events; i++) {
+ if (flags[i] & kFSEventStreamEventFlagRootChanged)
+ root_changed = true;
+ if (event_ids[i])
+ root_change_at = std::min(root_change_at, event_ids[i]);
+ paths.push_back(FilePath(
+ reinterpret_cast<char**>(event_paths)[i]).StripTrailingSeparators());
+ }
+
+ // Reinitialize the event stream if we find changes to the root. This is
+ // necessary since FSEvents doesn't report any events for the subtree after
+ // the directory to be watched gets created.
+ if (root_changed) {
+ // Resetting the event stream from within the callback fails (FSEvents spews
+ // bad file descriptor errors), so post a task to do the reset.
+ g_task_runner.Get().PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherFSEvents::UpdateEventStream, watcher,
+ root_change_at));
+ }
+
+ watcher->OnFilePathsChanged(paths);
+}
+
+} // namespace
+
+FilePathWatcherFSEvents::FilePathWatcherFSEvents() : fsevent_stream_(NULL) {
+}
+
+void FilePathWatcherFSEvents::OnFilePathsChanged(
+ const std::vector<FilePath>& paths) {
+ if (!message_loop()->BelongsToCurrentThread()) {
+ message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherFSEvents::OnFilePathsChanged, this, paths));
+ return;
+ }
+
+ DCHECK(message_loop()->BelongsToCurrentThread());
+ if (resolved_target_.empty())
+ return;
+
+ for (size_t i = 0; i < paths.size(); i++) {
+ if (resolved_target_.IsParent(paths[i]) || resolved_target_ == paths[i]) {
+ callback_.Run(target_, false);
+ return;
+ }
+ }
+}
+
+bool FilePathWatcherFSEvents::Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) {
+ DCHECK(resolved_target_.empty());
+ DCHECK(MessageLoopForIO::current());
+ DCHECK(!callback.is_null());
+
+ // This class could support non-recursive watches, but that is currently
+ // left to FilePathWatcherKQueue.
+ if (!recursive)
+ return false;
+
+ set_message_loop(MessageLoopProxy::current());
+ callback_ = callback;
+ target_ = path;
+
+ FSEventStreamEventId start_event = FSEventsGetCurrentEventId();
+ g_task_runner.Get().PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherFSEvents::StartEventStream, this, start_event));
+ return true;
+}
+
+void FilePathWatcherFSEvents::Cancel() {
+ if (callback_.is_null()) {
+ // Watch was never called, so exit.
+ set_cancelled();
+ return;
+ }
+
+ // Switch to the dispatch queue thread if necessary, so we can tear down
+ // the event stream.
+ if (!g_task_runner.Get().RunsTasksOnCurrentThread()) {
+ g_task_runner.Get().PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherFSEvents::CancelOnMessageLoopThread, this));
+ } else {
+ CancelOnMessageLoopThread();
+ }
+}
+
+void FilePathWatcherFSEvents::CancelOnMessageLoopThread() {
+ // For all other implementations, the "message loop thread" is the IO thread,
+ // as returned by message_loop(). This implementation, however, needs to
+ // cancel pending work on the Dipatch Queue thread.
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+
+ set_cancelled();
+ if (fsevent_stream_) {
+ DestroyEventStream();
+ callback_.Reset();
+ target_.clear();
+ resolved_target_.clear();
+ }
+}
+
+void FilePathWatcherFSEvents::UpdateEventStream(
+ FSEventStreamEventId start_event) {
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+
+ // It can happen that the watcher gets canceled while tasks that call this
+ // function are still in flight, so abort if this situation is detected.
+ if (is_cancelled() || resolved_target_.empty())
+ return;
+
+ if (fsevent_stream_)
+ DestroyEventStream();
+
+ ScopedCFTypeRef<CFStringRef> cf_path(CFStringCreateWithCString(
+ NULL, resolved_target_.value().c_str(), kCFStringEncodingMacHFS));
+ ScopedCFTypeRef<CFStringRef> cf_dir_path(CFStringCreateWithCString(
+ NULL, resolved_target_.DirName().value().c_str(),
+ kCFStringEncodingMacHFS));
+ CFStringRef paths_array[] = { cf_path.get(), cf_dir_path.get() };
+ ScopedCFTypeRef<CFArrayRef> watched_paths(CFArrayCreate(
+ NULL, reinterpret_cast<const void**>(paths_array), arraysize(paths_array),
+ &kCFTypeArrayCallBacks));
+
+ FSEventStreamContext context;
+ context.version = 0;
+ context.info = this;
+ context.retain = NULL;
+ context.release = NULL;
+ context.copyDescription = NULL;
+
+ fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context,
+ watched_paths,
+ start_event,
+ kEventLatencySeconds,
+ kFSEventStreamCreateFlagWatchRoot);
+ FSEventStreamSetDispatchQueue(fsevent_stream_,
+ g_task_runner.Get().GetDispatchQueue());
+
+ if (!FSEventStreamStart(fsevent_stream_))
+ message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true));
+}
+
+bool FilePathWatcherFSEvents::ResolveTargetPath() {
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+ FilePath resolved = ResolvePath(target_).StripTrailingSeparators();
+ bool changed = resolved != resolved_target_;
+ resolved_target_ = resolved;
+ if (resolved_target_.empty())
+ message_loop()->PostTask(FROM_HERE, Bind(callback_, target_, true));
+ return changed;
+}
+
+void FilePathWatcherFSEvents::DestroyEventStream() {
+ FSEventStreamStop(fsevent_stream_);
+ FSEventStreamInvalidate(fsevent_stream_);
+ FSEventStreamRelease(fsevent_stream_);
+ fsevent_stream_ = NULL;
+}
+
+void FilePathWatcherFSEvents::StartEventStream(
+ FSEventStreamEventId start_event) {
+ DCHECK(g_task_runner.Get().RunsTasksOnCurrentThread());
+ ResolveTargetPath();
+ UpdateEventStream(start_event);
+}
+
+FilePathWatcherFSEvents::~FilePathWatcherFSEvents() {}
+
+} // namespace base
diff --git a/chromium/base/files/file_path_watcher_fsevents.h b/chromium/base/files/file_path_watcher_fsevents.h
new file mode 100644
index 00000000000..5640b4d5497
--- /dev/null
+++ b/chromium/base/files/file_path_watcher_fsevents.h
@@ -0,0 +1,73 @@
+// Copyright 2014 The Chromium Authors. All 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_FILE_PATH_WATCHER_FSEVENTS_H_
+#define BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
+
+#include <CoreServices/CoreServices.h>
+
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+
+namespace base {
+
+// Mac-specific file watcher implementation based on FSEvents.
+// There are trade-offs between the FSEvents implementation and a kqueue
+// implementation. The biggest issues are that FSEvents on 10.6 sometimes drops
+// events and kqueue does not trigger for modifications to a file in a watched
+// directory. See file_path_watcher_mac.cc for the code that decides when to
+// use which one.
+class FilePathWatcherFSEvents : public FilePathWatcher::PlatformDelegate {
+ public:
+ FilePathWatcherFSEvents();
+
+ // Called from the FSEvents callback whenever there is a change to the paths.
+ void OnFilePathsChanged(const std::vector<FilePath>& paths);
+
+ // (Re-)Initialize the event stream to start reporting events from
+ // |start_event|.
+ void UpdateEventStream(FSEventStreamEventId start_event);
+
+ // Returns true if resolving the target path got a different result than
+ // last time it was done.
+ bool ResolveTargetPath();
+
+ // FilePathWatcher::PlatformDelegate overrides.
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ private:
+ virtual ~FilePathWatcherFSEvents();
+
+ // Destroy the event stream.
+ void DestroyEventStream();
+
+ // Start watching the FSEventStream.
+ void StartEventStream(FSEventStreamEventId start_event);
+
+ // Cleans up and stops the event stream.
+ virtual void CancelOnMessageLoopThread() OVERRIDE;
+
+ // Callback to notify upon changes.
+ FilePathWatcher::Callback callback_;
+
+ // Target path to watch (passed to callback).
+ FilePath target_;
+
+ // Target path with all symbolic links resolved.
+ FilePath resolved_target_;
+
+ // Backend stream we receive event callbacks from (strong reference).
+ FSEventStreamRef fsevent_stream_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherFSEvents);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_PATH_WATCHER_FSEVENTS_H_
diff --git a/chromium/base/files/file_path_watcher_kqueue.cc b/chromium/base/files/file_path_watcher_kqueue.cc
index e035f22caa5..c38e3448e73 100644
--- a/chromium/base/files/file_path_watcher_kqueue.cc
+++ b/chromium/base/files/file_path_watcher_kqueue.cc
@@ -2,19 +2,14 @@
// 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/files/file_path_watcher_kqueue.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/message_loop.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/strings/stringprintf.h"
// On some platforms these are not defined.
@@ -27,136 +22,18 @@
namespace base {
-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,
- const FilePathWatcher::Callback& callback) 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 or 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 uintptr_t FileDescriptorForPath(const FilePath& path);
-
- static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);
-
- // Closes |*fd| and sets |*fd| to -1.
- static void CloseFileDescriptor(uintptr_t* fd);
-
- // Returns true if kevent has open file descriptor.
- static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
- return event.ident != kNoFileDescriptor;
- }
-
- 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_;
- FilePathWatcher::Callback callback_;
- FilePath target_;
- int kqueue_;
+FilePathWatcherKQueue::FilePathWatcherKQueue() : kqueue_(-1) {}
- DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
-};
+FilePathWatcherKQueue::~FilePathWatcherKQueue() {}
-void FilePathWatcherImpl::ReleaseEvent(struct kevent& event) {
+void FilePathWatcherKQueue::ReleaseEvent(struct kevent& event) {
CloseFileDescriptor(&event.ident);
EventData* entry = EventDataForKevent(event);
delete entry;
event.udata = NULL;
}
-int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) {
+int FilePathWatcherKQueue::EventsForPath(FilePath path, EventVector* events) {
DCHECK(MessageLoopForIO::current());
// Make sure that we are working with a clean slate.
DCHECK(events->empty());
@@ -198,14 +75,14 @@ int FilePathWatcherImpl::EventsForPath(FilePath path, EventVector* events) {
return last_existing_entry;
}
-uintptr_t FilePathWatcherImpl::FileDescriptorForPath(const FilePath& path) {
+uintptr_t FilePathWatcherKQueue::FileDescriptorForPath(const FilePath& path) {
int fd = HANDLE_EINTR(open(path.value().c_str(), O_EVTONLY));
if (fd == -1)
return kNoFileDescriptor;
return fd;
}
-void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) {
+void FilePathWatcherKQueue::CloseFileDescriptor(uintptr_t* fd) {
if (*fd == kNoFileDescriptor) {
return;
}
@@ -216,7 +93,7 @@ void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) {
*fd = kNoFileDescriptor;
}
-bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents,
+bool FilePathWatcherKQueue::AreKeventValuesValid(struct kevent* kevents,
int count) {
if (count < 0) {
DPLOG(ERROR) << "kevent";
@@ -250,7 +127,7 @@ bool FilePathWatcherImpl::AreKeventValuesValid(struct kevent* kevents,
return valid;
}
-void FilePathWatcherImpl::HandleAttributesChange(
+void FilePathWatcherKQueue::HandleAttributesChange(
const EventVector::iterator& event,
bool* target_file_affected,
bool* update_watches) {
@@ -274,7 +151,7 @@ void FilePathWatcherImpl::HandleAttributesChange(
}
}
-void FilePathWatcherImpl::HandleDeleteOrMoveChange(
+void FilePathWatcherKQueue::HandleDeleteOrMoveChange(
const EventVector::iterator& event,
bool* target_file_affected,
bool* update_watches) {
@@ -290,7 +167,7 @@ void FilePathWatcherImpl::HandleDeleteOrMoveChange(
}
}
-void FilePathWatcherImpl::HandleCreateItemChange(
+void FilePathWatcherKQueue::HandleCreateItemChange(
const EventVector::iterator& event,
bool* target_file_affected,
bool* update_watches) {
@@ -310,7 +187,7 @@ void FilePathWatcherImpl::HandleCreateItemChange(
}
}
-bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) {
+bool FilePathWatcherKQueue::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.
@@ -351,7 +228,7 @@ bool FilePathWatcherImpl::UpdateWatches(bool* target_file_affected) {
return true;
}
-void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) {
+void FilePathWatcherKQueue::OnFileCanReadWithoutBlocking(int fd) {
DCHECK(MessageLoopForIO::current());
DCHECK_EQ(fd, kqueue_);
DCHECK(events_.size());
@@ -424,24 +301,24 @@ void FilePathWatcherImpl::OnFileCanReadWithoutBlocking(int fd) {
}
}
-void FilePathWatcherImpl::OnFileCanWriteWithoutBlocking(int fd) {
+void FilePathWatcherKQueue::OnFileCanWriteWithoutBlocking(int fd) {
NOTREACHED();
}
-void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
+void FilePathWatcherKQueue::WillDestroyCurrentMessageLoop() {
CancelOnMessageLoopThread();
}
-bool FilePathWatcherImpl::Watch(const FilePath& path,
- bool recursive,
- const FilePathWatcher::Callback& callback) {
+bool FilePathWatcherKQueue::Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) {
DCHECK(MessageLoopForIO::current());
DCHECK(target_.value().empty()); // Can only watch one path.
DCHECK(!callback.is_null());
DCHECK_EQ(kqueue_, -1);
if (recursive) {
- // Recursive watch is not supported on this platform.
+ // Recursive watch is not supported using kqueue.
NOTIMPLEMENTED();
return false;
}
@@ -478,7 +355,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
kqueue_, true, MessageLoopForIO::WATCH_READ, &kqueue_watcher_, this);
}
-void FilePathWatcherImpl::Cancel() {
+void FilePathWatcherKQueue::Cancel() {
base::MessageLoopProxy* proxy = io_message_loop_.get();
if (!proxy) {
set_cancelled();
@@ -486,13 +363,13 @@ void FilePathWatcherImpl::Cancel() {
}
if (!proxy->BelongsToCurrentThread()) {
proxy->PostTask(FROM_HERE,
- base::Bind(&FilePathWatcherImpl::Cancel, this));
+ base::Bind(&FilePathWatcherKQueue::Cancel, this));
return;
}
CancelOnMessageLoopThread();
}
-void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+void FilePathWatcherKQueue::CancelOnMessageLoopThread() {
DCHECK(MessageLoopForIO::current());
if (!is_cancelled()) {
set_cancelled();
@@ -509,10 +386,4 @@ void FilePathWatcherImpl::CancelOnMessageLoopThread() {
}
}
-} // namespace
-
-FilePathWatcher::FilePathWatcher() {
- impl_ = new FilePathWatcherImpl();
-}
-
} // namespace base
diff --git a/chromium/base/files/file_path_watcher_kqueue.h b/chromium/base/files/file_path_watcher_kqueue.h
new file mode 100644
index 00000000000..703fda67c4b
--- /dev/null
+++ b/chromium/base/files/file_path_watcher_kqueue.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All 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_FILE_PATH_WATCHER_KQUEUE_H_
+#define BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
+
+#include <sys/event.h>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/files/file_path_watcher.h"
+#include "base/message_loop/message_loop.h"
+#include "base/message_loop/message_loop_proxy.h"
+
+namespace base {
+
+// Mac-specific file watcher implementation based on kqueue.
+// The Linux and Windows versions are 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 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 FilePathWatcherKQueue : public FilePathWatcher::PlatformDelegate,
+ public MessageLoopForIO::Watcher,
+ public MessageLoop::DestructionObserver {
+ public:
+ FilePathWatcherKQueue();
+
+ // 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,
+ const FilePathWatcher::Callback& callback) OVERRIDE;
+ virtual void Cancel() OVERRIDE;
+
+ protected:
+ virtual ~FilePathWatcherKQueue();
+
+ 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 or 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 uintptr_t FileDescriptorForPath(const FilePath& path);
+
+ static const uintptr_t kNoFileDescriptor = static_cast<uintptr_t>(-1);
+
+ // Closes |*fd| and sets |*fd| to -1.
+ static void CloseFileDescriptor(uintptr_t* fd);
+
+ // Returns true if kevent has open file descriptor.
+ static bool IsKeventFileDescriptorOpen(const struct kevent& event) {
+ return event.ident != kNoFileDescriptor;
+ }
+
+ 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_;
+ FilePathWatcher::Callback callback_;
+ FilePath target_;
+ int kqueue_;
+
+ DISALLOW_COPY_AND_ASSIGN(FilePathWatcherKQueue);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_PATH_WATCHER_KQUEUE_H_
diff --git a/chromium/base/files/file_path_watcher_linux.cc b/chromium/base/files/file_path_watcher_linux.cc
index d5052e2e512..915ad50abb2 100644
--- a/chromium/base/files/file_path_watcher_linux.cc
+++ b/chromium/base/files/file_path_watcher_linux.cc
@@ -12,6 +12,7 @@
#include <unistd.h>
#include <algorithm>
+#include <map>
#include <set>
#include <utility>
#include <vector>
@@ -20,6 +21,7 @@
#include "base/containers/hash_tables.h"
#include "base/debug/trace_event.h"
#include "base/file_util.h"
+#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/location.h"
@@ -49,8 +51,8 @@ class InotifyReader {
// change. Returns kInvalidWatch on failure.
Watch AddWatch(const FilePath& path, FilePathWatcherImpl* watcher);
- // Remove |watch|. Returns true on success.
- bool RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
+ // Remove |watch| if it's valid.
+ void RemoveWatch(Watch watch, FilePathWatcherImpl* watcher);
// Callback for InotifyReaderTask.
void OnInotifyEvent(const inotify_event* event);
@@ -91,12 +93,21 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
// 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.
+ // the currently watched path for |fired_watch|.
+ //
+ // |created| is true if the object appears.
+ // |deleted| is true if the object disappears.
+ // |is_dir| is true if the object is a directory.
void OnFilePathChanged(InotifyReader::Watch fired_watch,
const FilePath::StringType& child,
- bool created);
+ bool created,
+ bool deleted,
+ bool is_dir);
+ protected:
+ virtual ~FilePathWatcherImpl() {}
+
+ private:
// 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,
@@ -106,36 +117,58 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
// Cancel the watch. This unregisters the instance with InotifyReader.
virtual void Cancel() OVERRIDE;
+ // Cleans up and stops observing the message_loop() thread.
+ virtual void CancelOnMessageLoopThread() 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) {}
+ explicit WatchEntry(const FilePath::StringType& dirname)
+ : watch(InotifyReader::kInvalidWatch),
+ subdir(dirname) {}
- InotifyReader::Watch watch_;
- FilePath::StringType subdir_;
- FilePath::StringType linkname_;
+ 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;
+ // that exists. Also calls UpdateRecursiveWatches() below.
+ void UpdateWatches();
+
+ // Reconfigure to recursively watch |target_| and all its sub-directories.
+ // - This is a no-op if the watch is not recursive.
+ // - If |target_| does not exist, then clear all the recursive watches.
+ // - Assuming |target_| exists, passing kInvalidWatch as |fired_watch| forces
+ // addition of recursive watches for |target_|.
+ // - Otherwise, only the directory associated with |fired_watch| and its
+ // sub-directories will be reconfigured.
+ void UpdateRecursiveWatches(InotifyReader::Watch fired_watch, bool is_dir);
+
+ // Enumerate recursively through |path| and add / update watches.
+ void UpdateRecursiveWatchesForPath(const FilePath& path);
+
+ // Do internal bookkeeping to update mappings between |watch| and its
+ // associated full path |path|.
+ void TrackWatchForRecursion(InotifyReader::Watch watch, const FilePath& path);
+
+ // Remove all the recursive watches.
+ void RemoveRecursiveWatches();
+
+ // |path| is a symlink to a non-existent target. Attempt to add a watch to
+ // the link target's parent directory. Returns true and update |watch_entry|
+ // on success.
+ bool AddWatchForBrokenSymlink(const FilePath& path, WatchEntry* watch_entry);
+
+ bool HasValidWatchVector() const;
// Callback to notify upon changes.
FilePathWatcher::Callback callback_;
@@ -143,11 +176,16 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
// The file or directory we're supposed to watch.
FilePath target_;
+ bool recursive_;
+
// 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_|.
+ // |target_| and always stores an empty next component name in |subdir|.
WatchVector watches_;
+ hash_map<InotifyReader::Watch, FilePath> recursive_paths_by_watch_;
+ std::map<FilePath, InotifyReader::Watch> recursive_watches_by_path_;
+
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
};
@@ -224,8 +262,8 @@ InotifyReader::InotifyReader()
shutdown_pipe_[1] = -1;
if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
thread_.message_loop()->PostTask(
- FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_,
- shutdown_pipe_[0]));
+ FROM_HERE,
+ Bind(&InotifyReaderCallback, this, inotify_fd_, shutdown_pipe_[0]));
valid_ = true;
}
}
@@ -255,7 +293,7 @@ InotifyReader::Watch InotifyReader::AddWatch(
AutoLock auto_lock(lock_);
Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
- IN_CREATE | IN_DELETE |
+ IN_ATTRIB | IN_CREATE | IN_DELETE |
IN_CLOSE_WRITE | IN_MOVE |
IN_ONLYDIR);
@@ -267,10 +305,9 @@ InotifyReader::Watch InotifyReader::AddWatch(
return watch;
}
-bool InotifyReader::RemoveWatch(Watch watch,
- FilePathWatcherImpl* watcher) {
- if (!valid_)
- return false;
+void InotifyReader::RemoveWatch(Watch watch, FilePathWatcherImpl* watcher) {
+ if (!valid_ || (watch == kInvalidWatch))
+ return;
AutoLock auto_lock(lock_);
@@ -278,10 +315,8 @@ bool InotifyReader::RemoveWatch(Watch watch,
if (watchers_[watch].empty()) {
watchers_.erase(watch);
- return (inotify_rm_watch(inotify_fd_, watch) == 0);
+ inotify_rm_watch(inotify_fd_, watch);
}
-
- return true;
}
void InotifyReader::OnInotifyEvent(const inotify_event* event) {
@@ -296,73 +331,96 @@ void InotifyReader::OnInotifyEvent(const inotify_event* event) {
++watcher) {
(*watcher)->OnFilePathChanged(event->wd,
child,
- event->mask & (IN_CREATE | IN_MOVED_TO));
+ event->mask & (IN_CREATE | IN_MOVED_TO),
+ event->mask & (IN_DELETE | IN_MOVED_FROM),
+ event->mask & IN_ISDIR);
}
}
-FilePathWatcherImpl::FilePathWatcherImpl() {
+FilePathWatcherImpl::FilePathWatcherImpl()
+ : recursive_(false) {
}
void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
const FilePath::StringType& child,
- bool created) {
+ bool created,
+ bool deleted,
+ bool is_dir) {
if (!message_loop()->BelongsToCurrentThread()) {
- // Switch to message_loop_ to access watches_ safely.
- message_loop()->PostTask(FROM_HERE,
- Bind(&FilePathWatcherImpl::OnFilePathChanged,
- this,
- fired_watch,
- child,
- created));
+ // Switch to message_loop() to access |watches_| safely.
+ message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&FilePathWatcherImpl::OnFilePathChanged, this,
+ fired_watch, child, created, deleted, is_dir));
+ return;
+ }
+
+ // Check to see if CancelOnMessageLoopThread() has already been called.
+ // May happen when code flow reaches here from the PostTask() above.
+ if (watches_.empty()) {
+ DCHECK(target_.empty());
return;
}
DCHECK(MessageLoopForIO::current());
+ DCHECK(HasValidWatchVector());
+
+ // Used below to avoid multiple recursive updates.
+ bool did_update = false;
// 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()) {
- callback_.Run(target_, true /* error */);
- return;
- }
+ for (size_t i = 0; i < watches_.size(); ++i) {
+ const WatchEntry& watch_entry = watches_[i];
+ if (fired_watch != watch_entry.watch)
+ continue;
+
+ // Check whether a path component of |target_| changed.
+ bool change_on_target_path =
+ child.empty() ||
+ (child == watch_entry.linkname) ||
+ (child == watch_entry.subdir);
+
+ // Check if the change references |target_| or a direct child of |target_|.
+ bool is_watch_for_target = watch_entry.subdir.empty();
+ bool target_changed =
+ (is_watch_for_target && (child == watch_entry.linkname)) ||
+ (is_watch_for_target && watch_entry.linkname.empty()) ||
+ (watch_entry.subdir == child && watches_[i + 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 && (created || deleted) && !did_update) {
+ UpdateWatches();
+ did_update = true;
+ }
- // 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 && PathExists(target_))) {
- callback_.Run(target_, false);
- 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 && deleted) ||
+ (change_on_target_path && created && PathExists(target_))) {
+ if (!did_update) {
+ UpdateRecursiveWatches(fired_watch, is_dir);
+ did_update = true;
}
+ callback_.Run(target_, false /* error */);
+ return;
}
}
+
+ if (ContainsKey(recursive_paths_by_watch_, fired_watch)) {
+ if (!did_update)
+ UpdateRecursiveWatches(fired_watch, is_dir);
+ callback_.Run(target_, false /* error */);
+ }
}
bool FilePathWatcherImpl::Watch(const FilePath& path,
@@ -370,120 +428,238 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
const FilePathWatcher::Callback& callback) {
DCHECK(target_.empty());
DCHECK(MessageLoopForIO::current());
- if (recursive) {
- // Recursive watch is not supported on this platform.
- NOTIMPLEMENTED();
- return false;
- }
set_message_loop(MessageLoopProxy::current().get());
callback_ = callback;
target_ = path;
+ recursive_ = recursive;
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();
+ for (size_t i = 1; i < comps.size(); ++i)
+ watches_.push_back(WatchEntry(comps[i]));
+ watches_.push_back(WatchEntry(FilePath::StringType()));
+ UpdateWatches();
+ return true;
}
void FilePathWatcherImpl::Cancel() {
if (callback_.is_null()) {
- // Watch was never called, or the |message_loop_| thread is already gone.
+ // 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_|.
+ // Switch to the message_loop() if necessary so we can access |watches_|.
if (!message_loop()->BelongsToCurrentThread()) {
message_loop()->PostTask(FROM_HERE,
Bind(&FilePathWatcher::CancelWatch,
- make_scoped_refptr(this)));
+ make_scoped_refptr(this)));
} else {
CancelOnMessageLoopThread();
}
}
void FilePathWatcherImpl::CancelOnMessageLoopThread() {
- if (!is_cancelled())
- set_cancelled();
+ DCHECK(message_loop()->BelongsToCurrentThread());
+ set_cancelled();
if (!callback_.is_null()) {
MessageLoop::current()->RemoveDestructionObserver(this);
callback_.Reset();
}
- 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);
- }
+ for (size_t i = 0; i < watches_.size(); ++i)
+ g_inotify_reader.Get().RemoveWatch(watches_[i].watch, this);
watches_.clear();
target_.clear();
+
+ if (recursive_)
+ RemoveRecursiveWatches();
}
void FilePathWatcherImpl::WillDestroyCurrentMessageLoop() {
CancelOnMessageLoopThread();
}
-bool FilePathWatcherImpl::UpdateWatches() {
- // Ensure this runs on the |message_loop_| exclusively in order to avoid
+void FilePathWatcherImpl::UpdateWatches() {
+ // Ensure this runs on the message_loop() exclusively in order to avoid
// concurrency issues.
DCHECK(message_loop()->BelongsToCurrentThread());
+ DCHECK(HasValidWatchVector());
// 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_;
+ for (size_t i = 0; i < watches_.size(); ++i) {
+ WatchEntry& watch_entry = watches_[i];
+ InotifyReader::Watch old_watch = watch_entry.watch;
+ watch_entry.watch = InotifyReader::kInvalidWatch;
+ watch_entry.linkname.clear();
if (path_valid) {
- watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this);
- if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) &&
- base::IsLink(path)) {
- FilePath link;
- if (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.
- }
+ watch_entry.watch = g_inotify_reader.Get().AddWatch(path, this);
+ if (watch_entry.watch == InotifyReader::kInvalidWatch) {
+ if (IsLink(path)) {
+ path_valid = AddWatchForBrokenSymlink(path, &watch_entry);
+ } else {
+ path_valid = false;
}
}
- 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_) {
+ if (old_watch != watch_entry.watch)
g_inotify_reader.Get().RemoveWatch(old_watch, this);
+ path = path.Append(watch_entry.subdir);
+ }
+
+ UpdateRecursiveWatches(InotifyReader::kInvalidWatch,
+ false /* is directory? */);
+}
+
+void FilePathWatcherImpl::UpdateRecursiveWatches(
+ InotifyReader::Watch fired_watch,
+ bool is_dir) {
+ if (!recursive_)
+ return;
+
+ if (!DirectoryExists(target_)) {
+ RemoveRecursiveWatches();
+ return;
+ }
+
+ // Check to see if this is a forced update or if some component of |target_|
+ // has changed. For these cases, redo the watches for |target_| and below.
+ if (!ContainsKey(recursive_paths_by_watch_, fired_watch)) {
+ UpdateRecursiveWatchesForPath(target_);
+ return;
+ }
+
+ // Underneath |target_|, only directory changes trigger watch updates.
+ if (!is_dir)
+ return;
+
+ const FilePath& changed_dir = recursive_paths_by_watch_[fired_watch];
+
+ std::map<FilePath, InotifyReader::Watch>::iterator start_it =
+ recursive_watches_by_path_.lower_bound(changed_dir);
+ std::map<FilePath, InotifyReader::Watch>::iterator end_it = start_it;
+ for (; end_it != recursive_watches_by_path_.end(); ++end_it) {
+ const FilePath& cur_path = end_it->first;
+ if (!changed_dir.IsParent(cur_path))
+ break;
+ if (!DirectoryExists(cur_path))
+ g_inotify_reader.Get().RemoveWatch(end_it->second, this);
+ }
+ recursive_watches_by_path_.erase(start_it, end_it);
+ UpdateRecursiveWatchesForPath(changed_dir);
+}
+
+void FilePathWatcherImpl::UpdateRecursiveWatchesForPath(const FilePath& path) {
+ DCHECK(recursive_);
+ DCHECK(!path.empty());
+ DCHECK(DirectoryExists(path));
+
+ // Note: SHOW_SYM_LINKS exposes symlinks as symlinks, so they are ignored
+ // rather than followed. Following symlinks can easily lead to the undesirable
+ // situation where the entire file system is being watched.
+ FileEnumerator enumerator(
+ path,
+ true /* recursive enumeration */,
+ FileEnumerator::DIRECTORIES | FileEnumerator::SHOW_SYM_LINKS);
+ for (FilePath current = enumerator.Next();
+ !current.empty();
+ current = enumerator.Next()) {
+ DCHECK(enumerator.GetInfo().IsDirectory());
+
+ if (!ContainsKey(recursive_watches_by_path_, current)) {
+ // Add new watches.
+ InotifyReader::Watch watch =
+ g_inotify_reader.Get().AddWatch(current, this);
+ TrackWatchForRecursion(watch, current);
+ } else {
+ // Update existing watches.
+ InotifyReader::Watch old_watch = recursive_watches_by_path_[current];
+ DCHECK_NE(InotifyReader::kInvalidWatch, old_watch);
+ InotifyReader::Watch watch =
+ g_inotify_reader.Get().AddWatch(current, this);
+ if (watch != old_watch) {
+ g_inotify_reader.Get().RemoveWatch(old_watch, this);
+ recursive_paths_by_watch_.erase(old_watch);
+ recursive_watches_by_path_.erase(current);
+ TrackWatchForRecursion(watch, current);
+ }
}
- path = path.Append(watch_entry->subdir_);
}
+}
+
+void FilePathWatcherImpl::TrackWatchForRecursion(InotifyReader::Watch watch,
+ const FilePath& path) {
+ DCHECK(recursive_);
+ DCHECK(!path.empty());
+ DCHECK(target_.IsParent(path));
+
+ if (watch == InotifyReader::kInvalidWatch)
+ return;
+
+ DCHECK(!ContainsKey(recursive_paths_by_watch_, watch));
+ DCHECK(!ContainsKey(recursive_watches_by_path_, path));
+ recursive_paths_by_watch_[watch] = path;
+ recursive_watches_by_path_[path] = watch;
+}
+
+void FilePathWatcherImpl::RemoveRecursiveWatches() {
+ if (!recursive_)
+ return;
+
+ for (hash_map<InotifyReader::Watch, FilePath>::const_iterator it =
+ recursive_paths_by_watch_.begin();
+ it != recursive_paths_by_watch_.end();
+ ++it) {
+ g_inotify_reader.Get().RemoveWatch(it->first, this);
+ }
+ recursive_paths_by_watch_.clear();
+ recursive_watches_by_path_.clear();
+}
+bool FilePathWatcherImpl::AddWatchForBrokenSymlink(const FilePath& path,
+ WatchEntry* watch_entry) {
+ DCHECK_EQ(InotifyReader::kInvalidWatch, watch_entry->watch);
+ FilePath link;
+ if (!ReadSymbolicLink(path, &link))
+ return false;
+
+ 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.
+ InotifyReader::Watch watch =
+ g_inotify_reader.Get().AddWatch(link.DirName(), this);
+ if (watch == InotifyReader::kInvalidWatch) {
+ // 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.
+ DPLOG(WARNING) << "Watch failed for " << link.DirName().value();
+ return false;
+ }
+ watch_entry->watch = watch;
+ watch_entry->linkname = link.BaseName().value();
return true;
}
+bool FilePathWatcherImpl::HasValidWatchVector() const {
+ if (watches_.empty())
+ return false;
+ for (size_t i = 0; i < watches_.size() - 1; ++i) {
+ if (watches_[i].subdir.empty())
+ return false;
+ }
+ return watches_[watches_.size() - 1].subdir.empty();
+}
+
} // namespace
FilePathWatcher::FilePathWatcher() {
diff --git a/chromium/base/files/file_path_watcher_mac.cc b/chromium/base/files/file_path_watcher_mac.cc
new file mode 100644
index 00000000000..54ca46d12bb
--- /dev/null
+++ b/chromium/base/files/file_path_watcher_mac.cc
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All 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/files/file_path_watcher_kqueue.h"
+
+#if !defined(OS_IOS)
+#include "base/files/file_path_watcher_fsevents.h"
+#endif
+
+namespace base {
+
+namespace {
+
+class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate {
+ public:
+ virtual bool Watch(const FilePath& path,
+ bool recursive,
+ const FilePathWatcher::Callback& callback) OVERRIDE {
+ // Use kqueue for non-recursive watches and FSEvents for recursive ones.
+ DCHECK(!impl_.get());
+ if (recursive) {
+ if (!FilePathWatcher::RecursiveWatchAvailable())
+ return false;
+#if !defined(OS_IOS)
+ impl_ = new FilePathWatcherFSEvents();
+#endif // OS_IOS
+ } else {
+ impl_ = new FilePathWatcherKQueue();
+ }
+ DCHECK(impl_.get());
+ return impl_->Watch(path, recursive, callback);
+ }
+
+ virtual void Cancel() OVERRIDE {
+ if (impl_)
+ impl_->Cancel();
+ set_cancelled();
+ }
+
+ virtual void CancelOnMessageLoopThread() OVERRIDE {
+ if (impl_)
+ impl_->Cancel();
+ set_cancelled();
+ }
+
+ protected:
+ virtual ~FilePathWatcherImpl() {}
+
+ scoped_refptr<PlatformDelegate> impl_;
+};
+
+} // namespace
+
+FilePathWatcher::FilePathWatcher() {
+ impl_ = new FilePathWatcherImpl();
+}
+
+} // namespace base
diff --git a/chromium/base/files/file_path_watcher_win.cc b/chromium/base/files/file_path_watcher_win.cc
index 2abbceacd29..6c10c12d313 100644
--- a/chromium/base/files/file_path_watcher_win.cc
+++ b/chromium/base/files/file_path_watcher_win.cc
@@ -6,6 +6,7 @@
#include "base/bind.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
@@ -96,6 +97,12 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
recursive_watch_ = recursive;
MessageLoop::current()->AddDestructionObserver(this);
+ File::Info file_info;
+ if (GetFileInfo(target_, &file_info)) {
+ last_modified_ = file_info.last_modified;
+ first_notification_ = Time::Now();
+ }
+
if (!UpdateWatch())
return false;
@@ -122,6 +129,7 @@ void FilePathWatcherImpl::Cancel() {
}
void FilePathWatcherImpl::CancelOnMessageLoopThread() {
+ DCHECK(message_loop()->BelongsToCurrentThread());
set_cancelled();
if (handle_ != INVALID_HANDLE_VALUE)
@@ -148,14 +156,23 @@ void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
}
// Check whether the event applies to |target_| and notify the callback.
- PlatformFileInfo file_info;
+ File::Info file_info;
bool file_exists = GetFileInfo(target_, &file_info);
- if (file_exists && (last_modified_.is_null() ||
- last_modified_ != file_info.last_modified)) {
+ if (recursive_watch_) {
+ // Only the mtime of |target_| is tracked but in a recursive watch,
+ // some other file or directory may have changed so all notifications
+ // are passed through. It is possible to figure out which file changed
+ // using ReadDirectoryChangesW() instead of FindFirstChangeNotification(),
+ // but that function is quite complicated:
+ // http://qualapps.blogspot.com/2010/05/understanding-readdirectorychangesw.html
+ callback_.Run(target_, false);
+ } else if (file_exists && (last_modified_.is_null() ||
+ last_modified_ != file_info.last_modified)) {
last_modified_ = file_info.last_modified;
first_notification_ = Time::Now();
callback_.Run(target_, false);
- } else if (file_exists && !first_notification_.is_null()) {
+ } else if (file_exists && last_modified_ == file_info.last_modified &&
+ !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
@@ -229,12 +246,6 @@ bool FilePathWatcherImpl::UpdateWatch() {
if (handle_ != INVALID_HANDLE_VALUE)
DestroyWatch();
- PlatformFileInfo file_info;
- if (GetFileInfo(target_, &file_info)) {
- last_modified_ = file_info.last_modified;
- first_notification_ = 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.
diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc
index 9d97c336aa6..0764ee98660 100644
--- a/chromium/base/files/file_posix.cc
+++ b/chromium/base/files/file_posix.cc
@@ -32,13 +32,11 @@ COMPILE_ASSERT(File::FROM_BEGIN == SEEK_SET &&
namespace {
#if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL)
-typedef struct stat stat_wrapper_t;
static int CallFstat(int fd, stat_wrapper_t *sb) {
base::ThreadRestrictions::AssertIOAllowed();
return fstat(fd, sb);
}
#else
-typedef struct stat64 stat_wrapper_t;
static int CallFstat(int fd, stat_wrapper_t *sb) {
base::ThreadRestrictions::AssertIOAllowed();
return fstat64(fd, sb);
@@ -119,13 +117,63 @@ static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
} // namespace
+void File::Info::FromStat(const stat_wrapper_t& stat_info) {
+ is_directory = S_ISDIR(stat_info.st_mode);
+ is_symbolic_link = S_ISLNK(stat_info.st_mode);
+ size = stat_info.st_size;
+
+#if defined(OS_LINUX)
+ time_t last_modified_sec = stat_info.st_mtim.tv_sec;
+ int64 last_modified_nsec = stat_info.st_mtim.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atim.tv_sec;
+ int64 last_accessed_nsec = stat_info.st_atim.tv_nsec;
+ time_t creation_time_sec = stat_info.st_ctim.tv_sec;
+ int64 creation_time_nsec = stat_info.st_ctim.tv_nsec;
+#elif defined(OS_ANDROID)
+ time_t last_modified_sec = stat_info.st_mtime;
+ int64 last_modified_nsec = stat_info.st_mtime_nsec;
+ time_t last_accessed_sec = stat_info.st_atime;
+ int64 last_accessed_nsec = stat_info.st_atime_nsec;
+ time_t creation_time_sec = stat_info.st_ctime;
+ int64 creation_time_nsec = stat_info.st_ctime_nsec;
+#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
+ time_t last_modified_sec = stat_info.st_mtimespec.tv_sec;
+ int64 last_modified_nsec = stat_info.st_mtimespec.tv_nsec;
+ time_t last_accessed_sec = stat_info.st_atimespec.tv_sec;
+ int64 last_accessed_nsec = stat_info.st_atimespec.tv_nsec;
+ time_t creation_time_sec = stat_info.st_ctimespec.tv_sec;
+ int64 creation_time_nsec = stat_info.st_ctimespec.tv_nsec;
+#else
+ time_t last_modified_sec = stat_info.st_mtime;
+ int64 last_modified_nsec = 0;
+ time_t last_accessed_sec = stat_info.st_atime;
+ int64 last_accessed_nsec = 0;
+ time_t creation_time_sec = stat_info.st_ctime;
+ int64 creation_time_nsec = 0;
+#endif
+
+ last_modified =
+ Time::FromTimeT(last_modified_sec) +
+ TimeDelta::FromMicroseconds(last_modified_nsec /
+ Time::kNanosecondsPerMicrosecond);
+
+ last_accessed =
+ Time::FromTimeT(last_accessed_sec) +
+ TimeDelta::FromMicroseconds(last_accessed_nsec /
+ Time::kNanosecondsPerMicrosecond);
+
+ creation_time =
+ Time::FromTimeT(creation_time_sec) +
+ TimeDelta::FromMicroseconds(creation_time_nsec /
+ Time::kNanosecondsPerMicrosecond);
+}
+
// NaCl doesn't implement system calls to open files directly.
#if !defined(OS_NACL)
// TODO(erikkay): does it make sense to support FLAG_EXCLUSIVE_* here?
-void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
+void File::InitializeUnsafe(const FilePath& name, uint32 flags) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(!IsValid());
- DCHECK(!(flags & FLAG_ASYNC));
int open_flags = 0;
if (flags & FLAG_CREATE)
@@ -135,6 +183,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
if (flags & FLAG_CREATE_ALWAYS) {
DCHECK(!open_flags);
+ DCHECK(flags & FLAG_WRITE);
open_flags = O_CREAT | O_TRUNC;
}
@@ -147,7 +196,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
NOTREACHED();
errno = EOPNOTSUPP;
- error_ = FILE_ERROR_FAILED;
+ error_details_ = FILE_ERROR_FAILED;
return;
}
@@ -191,47 +240,51 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
}
}
- if (descriptor >= 0 && (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)))
+ if (descriptor < 0) {
+ error_details_ = File::OSErrorToFileError(errno);
+ return;
+ }
+
+ if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
created_ = true;
- if ((descriptor >= 0) && (flags & FLAG_DELETE_ON_CLOSE))
+ if (flags & FLAG_DELETE_ON_CLOSE)
unlink(name.value().c_str());
- if (descriptor >= 0)
- error_ = FILE_OK;
- else
- error_ = File::OSErrorToFileError(errno);
-
- file_ = descriptor;
+ async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
+ error_details_ = FILE_OK;
+ file_.reset(descriptor);
}
#endif // !defined(OS_NACL)
bool File::IsValid() const {
- return file_ >= 0;
+ return file_.is_valid();
+}
+
+PlatformFile File::GetPlatformFile() const {
+ return file_.get();
}
PlatformFile File::TakePlatformFile() {
- PlatformFile file = file_;
- file_ = kInvalidPlatformFileValue;
- return file;
+ return file_.release();
}
void File::Close() {
- base::ThreadRestrictions::AssertIOAllowed();
if (!IsValid())
return;
- if (!IGNORE_EINTR(close(file_)))
- file_ = kInvalidPlatformFileValue;
+ base::ThreadRestrictions::AssertIOAllowed();
+ file_.reset();
}
int64 File::Seek(Whence whence, int64 offset) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
- if (file_ < 0 || offset < 0)
+ if (offset < 0)
return -1;
- return lseek(file_, static_cast<off_t>(offset), static_cast<int>(whence));
+ return lseek(file_.get(), static_cast<off_t>(offset),
+ static_cast<int>(whence));
}
int File::Read(int64 offset, char* data, int size) {
@@ -243,7 +296,7 @@ int File::Read(int64 offset, char* data, int size) {
int bytes_read = 0;
int rv;
do {
- rv = HANDLE_EINTR(pread(file_, data + bytes_read,
+ rv = HANDLE_EINTR(pread(file_.get(), data + bytes_read,
size - bytes_read, offset + bytes_read));
if (rv <= 0)
break;
@@ -263,7 +316,7 @@ int File::ReadAtCurrentPos(char* data, int size) {
int bytes_read = 0;
int rv;
do {
- rv = HANDLE_EINTR(read(file_, data, size));
+ rv = HANDLE_EINTR(read(file_.get(), data + bytes_read, size - bytes_read));
if (rv <= 0)
break;
@@ -277,7 +330,7 @@ int File::ReadNoBestEffort(int64 offset, char* data, int size) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
- return HANDLE_EINTR(pread(file_, data, size, offset));
+ return HANDLE_EINTR(pread(file_.get(), data, size, offset));
}
int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
@@ -286,13 +339,13 @@ int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
if (size < 0)
return -1;
- return HANDLE_EINTR(read(file_, data, size));
+ return HANDLE_EINTR(read(file_.get(), data, size));
}
int File::Write(int64 offset, const char* data, int size) {
base::ThreadRestrictions::AssertIOAllowed();
- if (IsOpenAppend(file_))
+ if (IsOpenAppend(file_.get()))
return WriteAtCurrentPos(data, size);
DCHECK(IsValid());
@@ -302,7 +355,7 @@ int File::Write(int64 offset, const char* data, int size) {
int bytes_written = 0;
int rv;
do {
- rv = HANDLE_EINTR(pwrite(file_, data + bytes_written,
+ rv = HANDLE_EINTR(pwrite(file_.get(), data + bytes_written,
size - bytes_written, offset + bytes_written));
if (rv <= 0)
break;
@@ -322,7 +375,8 @@ int File::WriteAtCurrentPos(const char* data, int size) {
int bytes_written = 0;
int rv;
do {
- rv = HANDLE_EINTR(write(file_, data, size));
+ rv = HANDLE_EINTR(write(file_.get(), data + bytes_written,
+ size - bytes_written));
if (rv <= 0)
break;
@@ -338,19 +392,29 @@ int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
if (size < 0)
return -1;
- return HANDLE_EINTR(write(file_, data, size));
+ return HANDLE_EINTR(write(file_.get(), data, size));
}
-bool File::Truncate(int64 length) {
+int64 File::GetLength() {
+ DCHECK(IsValid());
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file_.get(), &file_info))
+ return false;
+
+ return file_info.st_size;
+}
+
+bool File::SetLength(int64 length) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
- return !CallFtruncate(file_, length);
+ return !CallFtruncate(file_.get(), length);
}
bool File::Flush() {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
- return !CallFsync(file_);
+ return !CallFsync(file_.get());
}
bool File::SetTimes(Time last_access_time, Time last_modified_time) {
@@ -361,72 +425,26 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) {
times[0] = last_access_time.ToTimeVal();
times[1] = last_modified_time.ToTimeVal();
- return !CallFutimes(file_, times);
+ return !CallFutimes(file_.get(), times);
}
bool File::GetInfo(Info* info) {
DCHECK(IsValid());
stat_wrapper_t file_info;
- if (CallFstat(file_, &file_info))
+ if (CallFstat(file_.get(), &file_info))
return false;
- info->is_directory = S_ISDIR(file_info.st_mode);
- info->is_symbolic_link = S_ISLNK(file_info.st_mode);
- info->size = file_info.st_size;
-
-#if defined(OS_LINUX)
- const time_t last_modified_sec = file_info.st_mtim.tv_sec;
- const int64 last_modified_nsec = file_info.st_mtim.tv_nsec;
- const time_t last_accessed_sec = file_info.st_atim.tv_sec;
- const int64 last_accessed_nsec = file_info.st_atim.tv_nsec;
- const time_t creation_time_sec = file_info.st_ctim.tv_sec;
- const int64 creation_time_nsec = file_info.st_ctim.tv_nsec;
-#elif defined(OS_ANDROID)
- const time_t last_modified_sec = file_info.st_mtime;
- const int64 last_modified_nsec = file_info.st_mtime_nsec;
- const time_t last_accessed_sec = file_info.st_atime;
- const int64 last_accessed_nsec = file_info.st_atime_nsec;
- const time_t creation_time_sec = file_info.st_ctime;
- const int64 creation_time_nsec = file_info.st_ctime_nsec;
-#elif defined(OS_MACOSX) || defined(OS_IOS) || defined(OS_BSD)
- const time_t last_modified_sec = file_info.st_mtimespec.tv_sec;
- const int64 last_modified_nsec = file_info.st_mtimespec.tv_nsec;
- const time_t last_accessed_sec = file_info.st_atimespec.tv_sec;
- const int64 last_accessed_nsec = file_info.st_atimespec.tv_nsec;
- const time_t creation_time_sec = file_info.st_ctimespec.tv_sec;
- const int64 creation_time_nsec = file_info.st_ctimespec.tv_nsec;
-#else
- // TODO(gavinp): Investigate a good high resolution option for OS_NACL.
- const time_t last_modified_sec = file_info.st_mtime;
- const int64 last_modified_nsec = 0;
- const time_t last_accessed_sec = file_info.st_atime;
- const int64 last_accessed_nsec = 0;
- const time_t creation_time_sec = file_info.st_ctime;
- const int64 creation_time_nsec = 0;
-#endif
-
- info->last_modified =
- base::Time::FromTimeT(last_modified_sec) +
- base::TimeDelta::FromMicroseconds(last_modified_nsec /
- base::Time::kNanosecondsPerMicrosecond);
- info->last_accessed =
- base::Time::FromTimeT(last_accessed_sec) +
- base::TimeDelta::FromMicroseconds(last_accessed_nsec /
- base::Time::kNanosecondsPerMicrosecond);
- info->creation_time =
- base::Time::FromTimeT(creation_time_sec) +
- base::TimeDelta::FromMicroseconds(creation_time_nsec /
- base::Time::kNanosecondsPerMicrosecond);
+ info->FromStat(file_info);
return true;
}
File::Error File::Lock() {
- return CallFctnlFlock(file_, true);
+ return CallFctnlFlock(file_.get(), true);
}
File::Error File::Unlock() {
- return CallFctnlFlock(file_, false);
+ return CallFctnlFlock(file_.get(), false);
}
// Static.
@@ -463,8 +481,8 @@ File::Error File::OSErrorToFileError(int saved_errno) {
}
void File::SetPlatformFile(PlatformFile file) {
- DCHECK_EQ(file_, kInvalidPlatformFileValue);
- file_ = file;
+ DCHECK(!file_.is_valid());
+ file_.reset(file);
}
} // namespace base
diff --git a/chromium/base/files/file_proxy.cc b/chromium/base/files/file_proxy.cc
new file mode 100644
index 00000000000..291b98d1117
--- /dev/null
+++ b/chromium/base/files/file_proxy.cc
@@ -0,0 +1,359 @@
+// Copyright 2014 The Chromium Authors. All 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_proxy.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/location.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/task_runner.h"
+#include "base/task_runner_util.h"
+
+namespace {
+
+void FileDeleter(base::File file) {
+}
+
+} // namespace
+
+namespace base {
+
+class FileHelper {
+ public:
+ FileHelper(FileProxy* proxy, File file)
+ : file_(file.Pass()),
+ error_(File::FILE_ERROR_FAILED),
+ task_runner_(proxy->task_runner()),
+ proxy_(AsWeakPtr(proxy)) {
+ }
+
+ void PassFile() {
+ if (proxy_)
+ proxy_->SetFile(file_.Pass());
+ else if (file_.IsValid())
+ task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
+ }
+
+ protected:
+ File file_;
+ File::Error error_;
+
+ private:
+ scoped_refptr<TaskRunner> task_runner_;
+ WeakPtr<FileProxy> proxy_;
+ DISALLOW_COPY_AND_ASSIGN(FileHelper);
+};
+
+namespace {
+
+class GenericFileHelper : public FileHelper {
+ public:
+ GenericFileHelper(FileProxy* proxy, File file)
+ : FileHelper(proxy, file.Pass()) {
+ }
+
+ void Close() {
+ file_.Close();
+ error_ = File::FILE_OK;
+ }
+
+ void SetTimes(Time last_access_time, Time last_modified_time) {
+ bool rv = file_.SetTimes(last_access_time, last_modified_time);
+ error_ = rv ? File::FILE_OK : File::FILE_ERROR_FAILED;
+ }
+
+ void SetLength(int64 length) {
+ if (file_.SetLength(length))
+ error_ = File::FILE_OK;
+ }
+
+ void Flush() {
+ if (file_.Flush())
+ error_ = File::FILE_OK;
+ }
+
+ void Reply(const FileProxy::StatusCallback& callback) {
+ PassFile();
+ if (!callback.is_null())
+ callback.Run(error_);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GenericFileHelper);
+};
+
+class CreateOrOpenHelper : public FileHelper {
+ public:
+ CreateOrOpenHelper(FileProxy* proxy, File file)
+ : FileHelper(proxy, file.Pass()) {
+ }
+
+ void RunWork(const FilePath& file_path, int file_flags) {
+ file_.Initialize(file_path, file_flags);
+ error_ = file_.IsValid() ? File::FILE_OK : file_.error_details();
+ }
+
+ void Reply(const FileProxy::StatusCallback& callback) {
+ DCHECK(!callback.is_null());
+ PassFile();
+ callback.Run(error_);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CreateOrOpenHelper);
+};
+
+class CreateTemporaryHelper : public FileHelper {
+ public:
+ CreateTemporaryHelper(FileProxy* proxy, File file)
+ : FileHelper(proxy, file.Pass()) {
+ }
+
+ void RunWork(uint32 additional_file_flags) {
+ // TODO(darin): file_util should have a variant of CreateTemporaryFile
+ // that returns a FilePath and a File.
+ if (!CreateTemporaryFile(&file_path_)) {
+ // TODO(davidben): base::CreateTemporaryFile should preserve the error
+ // code.
+ error_ = File::FILE_ERROR_FAILED;
+ return;
+ }
+
+ uint32 file_flags = File::FLAG_WRITE |
+ File::FLAG_TEMPORARY |
+ File::FLAG_CREATE_ALWAYS |
+ additional_file_flags;
+
+ file_.Initialize(file_path_, file_flags);
+ if (file_.IsValid()) {
+ error_ = File::FILE_OK;
+ } else {
+ error_ = file_.error_details();
+ DeleteFile(file_path_, false);
+ file_path_.clear();
+ }
+ }
+
+ void Reply(const FileProxy::CreateTemporaryCallback& callback) {
+ DCHECK(!callback.is_null());
+ PassFile();
+ callback.Run(error_, file_path_);
+ }
+
+ private:
+ FilePath file_path_;
+ DISALLOW_COPY_AND_ASSIGN(CreateTemporaryHelper);
+};
+
+class GetInfoHelper : public FileHelper {
+ public:
+ GetInfoHelper(FileProxy* proxy, File file)
+ : FileHelper(proxy, file.Pass()) {
+ }
+
+ void RunWork() {
+ if (file_.GetInfo(&file_info_))
+ error_ = File::FILE_OK;
+ }
+
+ void Reply(const FileProxy::GetFileInfoCallback& callback) {
+ PassFile();
+ DCHECK(!callback.is_null());
+ callback.Run(error_, file_info_);
+ }
+
+ private:
+ File::Info file_info_;
+ DISALLOW_COPY_AND_ASSIGN(GetInfoHelper);
+};
+
+class ReadHelper : public FileHelper {
+ public:
+ ReadHelper(FileProxy* proxy, File file, int bytes_to_read)
+ : FileHelper(proxy, file.Pass()),
+ buffer_(new char[bytes_to_read]),
+ bytes_to_read_(bytes_to_read),
+ bytes_read_(0) {
+ }
+
+ void RunWork(int64 offset) {
+ bytes_read_ = file_.Read(offset, buffer_.get(), bytes_to_read_);
+ error_ = (bytes_read_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
+ }
+
+ void Reply(const FileProxy::ReadCallback& callback) {
+ PassFile();
+ DCHECK(!callback.is_null());
+ callback.Run(error_, buffer_.get(), bytes_read_);
+ }
+
+ private:
+ scoped_ptr<char[]> buffer_;
+ int bytes_to_read_;
+ int bytes_read_;
+ DISALLOW_COPY_AND_ASSIGN(ReadHelper);
+};
+
+class WriteHelper : public FileHelper {
+ public:
+ WriteHelper(FileProxy* proxy,
+ File file,
+ const char* buffer, int bytes_to_write)
+ : FileHelper(proxy, file.Pass()),
+ buffer_(new char[bytes_to_write]),
+ bytes_to_write_(bytes_to_write),
+ bytes_written_(0) {
+ memcpy(buffer_.get(), buffer, bytes_to_write);
+ }
+
+ void RunWork(int64 offset) {
+ bytes_written_ = file_.Write(offset, buffer_.get(), bytes_to_write_);
+ error_ = (bytes_written_ < 0) ? File::FILE_ERROR_FAILED : File::FILE_OK;
+ }
+
+ void Reply(const FileProxy::WriteCallback& callback) {
+ PassFile();
+ if (!callback.is_null())
+ callback.Run(error_, bytes_written_);
+ }
+
+ private:
+ scoped_ptr<char[]> buffer_;
+ int bytes_to_write_;
+ int bytes_written_;
+ DISALLOW_COPY_AND_ASSIGN(WriteHelper);
+};
+
+} // namespace
+
+FileProxy::FileProxy(TaskRunner* task_runner) : task_runner_(task_runner) {
+}
+
+FileProxy::~FileProxy() {
+ if (file_.IsValid())
+ task_runner_->PostTask(FROM_HERE, Bind(&FileDeleter, Passed(&file_)));
+}
+
+bool FileProxy::CreateOrOpen(const FilePath& file_path,
+ uint32 file_flags,
+ const StatusCallback& callback) {
+ DCHECK(!file_.IsValid());
+ CreateOrOpenHelper* helper = new CreateOrOpenHelper(this, File());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&CreateOrOpenHelper::RunWork, Unretained(helper), file_path,
+ file_flags),
+ Bind(&CreateOrOpenHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::CreateTemporary(uint32 additional_file_flags,
+ const CreateTemporaryCallback& callback) {
+ DCHECK(!file_.IsValid());
+ CreateTemporaryHelper* helper = new CreateTemporaryHelper(this, File());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&CreateTemporaryHelper::RunWork, Unretained(helper),
+ additional_file_flags),
+ Bind(&CreateTemporaryHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::IsValid() const {
+ return file_.IsValid();
+}
+
+void FileProxy::SetFile(File file) {
+ DCHECK(!file_.IsValid());
+ file_ = file.Pass();
+}
+
+File FileProxy::TakeFile() {
+ return file_.Pass();
+}
+
+PlatformFile FileProxy::GetPlatformFile() const {
+ return file_.GetPlatformFile();
+}
+
+bool FileProxy::Close(const StatusCallback& callback) {
+ DCHECK(file_.IsValid());
+ GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GenericFileHelper::Close, Unretained(helper)),
+ Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::GetInfo(const GetFileInfoCallback& callback) {
+ DCHECK(file_.IsValid());
+ GetInfoHelper* helper = new GetInfoHelper(this, file_.Pass());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GetInfoHelper::RunWork, Unretained(helper)),
+ Bind(&GetInfoHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::Read(int64 offset,
+ int bytes_to_read,
+ const ReadCallback& callback) {
+ DCHECK(file_.IsValid());
+ if (bytes_to_read < 0)
+ return false;
+
+ ReadHelper* helper = new ReadHelper(this, file_.Pass(), bytes_to_read);
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&ReadHelper::RunWork, Unretained(helper), offset),
+ Bind(&ReadHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::Write(int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ const WriteCallback& callback) {
+ DCHECK(file_.IsValid());
+ if (bytes_to_write <= 0 || buffer == NULL)
+ return false;
+
+ WriteHelper* helper =
+ new WriteHelper(this, file_.Pass(), buffer, bytes_to_write);
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&WriteHelper::RunWork, Unretained(helper), offset),
+ Bind(&WriteHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::SetTimes(Time last_access_time,
+ Time last_modified_time,
+ const StatusCallback& callback) {
+ DCHECK(file_.IsValid());
+ GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GenericFileHelper::SetTimes, Unretained(helper), last_access_time,
+ last_modified_time),
+ Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::SetLength(int64 length, const StatusCallback& callback) {
+ DCHECK(file_.IsValid());
+ GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GenericFileHelper::SetLength, Unretained(helper), length),
+ Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+}
+
+bool FileProxy::Flush(const StatusCallback& callback) {
+ DCHECK(file_.IsValid());
+ GenericFileHelper* helper = new GenericFileHelper(this, file_.Pass());
+ return task_runner_->PostTaskAndReply(
+ FROM_HERE,
+ Bind(&GenericFileHelper::Flush, Unretained(helper)),
+ Bind(&GenericFileHelper::Reply, Owned(helper), callback));
+}
+
+} // namespace base
diff --git a/chromium/base/files/file_proxy.h b/chromium/base/files/file_proxy.h
new file mode 100644
index 00000000000..f990d044d37
--- /dev/null
+++ b/chromium/base/files/file_proxy.h
@@ -0,0 +1,142 @@
+// Copyright 2014 The Chromium Authors. All 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_FILE_PROXY_H_
+#define BASE_FILES_FILE_PROXY_H_
+
+#include "base/base_export.h"
+#include "base/callback_forward.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace tracked_objects {
+class Location;
+};
+
+namespace base {
+
+class TaskRunner;
+class Time;
+
+// This class provides asynchronous access to a File. All methods follow the
+// same rules of the equivalent File method, as they are implemented by bouncing
+// the operation to File using a TaskRunner.
+//
+// This class performs automatic proxying to close the underlying file at
+// destruction.
+//
+// The TaskRunner is in charge of any sequencing of the operations, but a single
+// operation can be proxied at a time, regardless of the use of a callback.
+// In other words, having a sequence like
+//
+// proxy.Write(...);
+// proxy.Write(...);
+//
+// means the second Write will always fail.
+class BASE_EXPORT FileProxy : public SupportsWeakPtr<FileProxy> {
+ public:
+ // This callback is used by methods that report only an error code. It is
+ // valid to pass a null callback to some functions that takes a
+ // StatusCallback, in which case the operation will complete silently.
+ typedef Callback<void(File::Error)> StatusCallback;
+
+ typedef Callback<void(File::Error,
+ const FilePath&)> CreateTemporaryCallback;
+ typedef Callback<void(File::Error,
+ const File::Info&)> GetFileInfoCallback;
+ typedef Callback<void(File::Error,
+ const char* data,
+ int bytes_read)> ReadCallback;
+ typedef Callback<void(File::Error,
+ int bytes_written)> WriteCallback;
+
+ FileProxy();
+ explicit FileProxy(TaskRunner* task_runner);
+ ~FileProxy();
+
+ // Creates or opens a file with the given flags. It is invalid to pass a null
+ // callback. If File::FLAG_CREATE is set in |file_flags| it always tries to
+ // create a new file at the given |file_path| and fails if the file already
+ // exists.
+ //
+ // This returns false if task posting to |task_runner| has failed.
+ bool CreateOrOpen(const FilePath& file_path,
+ uint32 file_flags,
+ const StatusCallback& callback);
+
+ // Creates a temporary file for writing. The path and an open file 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:
+ // File::FLAG_CREATE_ALWAYS
+ // File::FLAG_WRITE
+ // File::FLAG_TEMPORARY.
+ //
+ // This returns false if task posting to |task_runner| has failed.
+ bool CreateTemporary(uint32 additional_file_flags,
+ const CreateTemporaryCallback& callback);
+
+ // Returns true if the underlying |file_| is valid.
+ bool IsValid() const;
+
+ // Returns true if a new file was created (or an old one truncated to zero
+ // length to simulate a new file), and false otherwise.
+ bool created() const { return file_.created(); }
+
+ // Claims ownership of |file|. It is an error to call this method when
+ // IsValid() returns true.
+ void SetFile(File file);
+
+ File TakeFile();
+
+ PlatformFile GetPlatformFile() const;
+
+ // Proxies File::Close. The callback can be null.
+ // This returns false if task posting to |task_runner| has failed.
+ bool Close(const StatusCallback& callback);
+
+ // Proxies File::GetInfo. The callback can't be null.
+ // This returns false if task posting to |task_runner| has failed.
+ bool GetInfo(const GetFileInfoCallback& callback);
+
+ // Proxies File::Read. The callback can't be null.
+ // This returns false if |bytes_to_read| is less than zero, or
+ // if task posting to |task_runner| has failed.
+ bool Read(int64 offset, int bytes_to_read, const ReadCallback& callback);
+
+ // Proxies File::Write. The callback can be null.
+ // This returns false if |bytes_to_write| is less than or equal to zero,
+ // if |buffer| is NULL, or if task posting to |task_runner| has failed.
+ bool Write(int64 offset,
+ const char* buffer,
+ int bytes_to_write,
+ const WriteCallback& callback);
+
+ // Proxies File::SetTimes. The callback can be null.
+ // This returns false if task posting to |task_runner| has failed.
+ bool SetTimes(Time last_access_time,
+ Time last_modified_time,
+ const StatusCallback& callback);
+
+ // Proxies File::SetLength. The callback can be null.
+ // This returns false if task posting to |task_runner| has failed.
+ bool SetLength(int64 length, const StatusCallback& callback);
+
+ // Proxies File::Flush. The callback can be null.
+ // This returns false if task posting to |task_runner| has failed.
+ bool Flush(const StatusCallback& callback);
+
+ private:
+ friend class FileHelper;
+ TaskRunner* task_runner() { return task_runner_.get(); }
+
+ scoped_refptr<TaskRunner> task_runner_;
+ File file_;
+ DISALLOW_COPY_AND_ASSIGN(FileProxy);
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_PROXY_H_
diff --git a/chromium/base/files/file_proxy_unittest.cc b/chromium/base/files/file_proxy_unittest.cc
new file mode 100644
index 00000000000..9be8abf67d2
--- /dev/null
+++ b/chromium/base/files/file_proxy_unittest.cc
@@ -0,0 +1,370 @@
+// Copyright 2014 The Chromium Authors. All 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_proxy.h"
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class FileProxyTest : public testing::Test {
+ public:
+ FileProxyTest()
+ : file_thread_("FileProxyTestFileThread"),
+ error_(File::FILE_OK),
+ bytes_written_(-1),
+ weak_factory_(this) {}
+
+ virtual void SetUp() OVERRIDE {
+ ASSERT_TRUE(dir_.CreateUniqueTempDir());
+ ASSERT_TRUE(file_thread_.Start());
+ }
+
+ void DidFinish(File::Error error) {
+ error_ = error;
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void DidCreateOrOpen(File::Error error) {
+ error_ = error;
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void DidCreateTemporary(File::Error error,
+ const FilePath& path) {
+ error_ = error;
+ path_ = path;
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void DidGetFileInfo(File::Error error,
+ const File::Info& file_info) {
+ error_ = error;
+ file_info_ = file_info;
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void DidRead(File::Error error,
+ const char* data,
+ int bytes_read) {
+ error_ = error;
+ buffer_.resize(bytes_read);
+ memcpy(&buffer_[0], data, bytes_read);
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ void DidWrite(File::Error error,
+ int bytes_written) {
+ error_ = error;
+ bytes_written_ = bytes_written;
+ MessageLoop::current()->QuitWhenIdle();
+ }
+
+ protected:
+ void CreateProxy(uint32 flags, FileProxy* proxy) {
+ proxy->CreateOrOpen(
+ test_path(), flags,
+ Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_TRUE(proxy->IsValid());
+ }
+
+ 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"); }
+
+ MessageLoopForIO message_loop_;
+ Thread file_thread_;
+
+ ScopedTempDir dir_;
+ File::Error error_;
+ FilePath path_;
+ File::Info file_info_;
+ std::vector<char> buffer_;
+ int bytes_written_;
+ WeakPtrFactory<FileProxyTest> weak_factory_;
+};
+
+TEST_F(FileProxyTest, CreateOrOpen_Create) {
+ FileProxy proxy(file_task_runner());
+ proxy.CreateOrOpen(
+ test_path(),
+ File::FLAG_CREATE | File::FLAG_READ,
+ Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(File::FILE_OK, error_);
+ EXPECT_TRUE(proxy.IsValid());
+ EXPECT_TRUE(proxy.created());
+ EXPECT_TRUE(PathExists(test_path()));
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_Open) {
+ // Creates a file.
+ base::WriteFile(test_path(), NULL, 0);
+ ASSERT_TRUE(PathExists(test_path()));
+
+ // Opens the created file.
+ FileProxy proxy(file_task_runner());
+ proxy.CreateOrOpen(
+ test_path(),
+ File::FLAG_OPEN | File::FLAG_READ,
+ Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(File::FILE_OK, error_);
+ EXPECT_TRUE(proxy.IsValid());
+ EXPECT_FALSE(proxy.created());
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_OpenNonExistent) {
+ FileProxy proxy(file_task_runner());
+ proxy.CreateOrOpen(
+ test_path(),
+ File::FLAG_OPEN | File::FLAG_READ,
+ Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::FILE_ERROR_NOT_FOUND, error_);
+ EXPECT_FALSE(proxy.IsValid());
+ EXPECT_FALSE(proxy.created());
+ EXPECT_FALSE(PathExists(test_path()));
+}
+
+TEST_F(FileProxyTest, CreateOrOpen_AbandonedCreate) {
+ bool prev = ThreadRestrictions::SetIOAllowed(false);
+ {
+ FileProxy proxy(file_task_runner());
+ proxy.CreateOrOpen(
+ test_path(),
+ File::FLAG_CREATE | File::FLAG_READ,
+ Bind(&FileProxyTest::DidCreateOrOpen, weak_factory_.GetWeakPtr()));
+ }
+ MessageLoop::current()->Run();
+ ThreadRestrictions::SetIOAllowed(prev);
+
+ EXPECT_TRUE(PathExists(test_path()));
+}
+
+TEST_F(FileProxyTest, Close) {
+ // Creates a file.
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
+
+#if defined(OS_WIN)
+ // This fails on Windows if the file is not closed.
+ EXPECT_FALSE(base::Move(test_path(), test_dir_path().AppendASCII("new")));
+#endif
+
+ proxy.Close(Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::FILE_OK, error_);
+ EXPECT_FALSE(proxy.IsValid());
+
+ // Now it should pass on all platforms.
+ EXPECT_TRUE(base::Move(test_path(), test_dir_path().AppendASCII("new")));
+}
+
+TEST_F(FileProxyTest, CreateTemporary) {
+ {
+ FileProxy proxy(file_task_runner());
+ proxy.CreateTemporary(
+ 0 /* additional_file_flags */,
+ Bind(&FileProxyTest::DidCreateTemporary, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ EXPECT_TRUE(proxy.IsValid());
+ EXPECT_EQ(File::FILE_OK, error_);
+ EXPECT_TRUE(PathExists(path_));
+
+ // The file should be writable.
+ proxy.Write(0, "test", 4,
+ Bind(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::FILE_OK, error_);
+ EXPECT_EQ(4, bytes_written_);
+ }
+
+ // Make sure the written data can be read from the returned path.
+ std::string data;
+ EXPECT_TRUE(ReadFileToString(path_, &data));
+ EXPECT_EQ("test", data);
+
+ // Make sure we can & do delete the created file to prevent leaks on the bots.
+ EXPECT_TRUE(base::DeleteFile(path_, false));
+}
+
+TEST_F(FileProxyTest, SetAndTake) {
+ File file(test_path(), File::FLAG_CREATE | File::FLAG_READ);
+ ASSERT_TRUE(file.IsValid());
+ FileProxy proxy(file_task_runner());
+ EXPECT_FALSE(proxy.IsValid());
+ proxy.SetFile(file.Pass());
+ EXPECT_TRUE(proxy.IsValid());
+ EXPECT_FALSE(file.IsValid());
+
+ file = proxy.TakeFile();
+ EXPECT_FALSE(proxy.IsValid());
+ EXPECT_TRUE(file.IsValid());
+}
+
+TEST_F(FileProxyTest, GetInfo) {
+ // Setup.
+ ASSERT_EQ(4, base::WriteFile(test_path(), "test", 4));
+ File::Info expected_info;
+ GetFileInfo(test_path(), &expected_info);
+
+ // Run.
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
+ proxy.GetInfo(
+ Bind(&FileProxyTest::DidGetFileInfo, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ EXPECT_EQ(File::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.creation_time, file_info_.creation_time);
+}
+
+TEST_F(FileProxyTest, Read) {
+ // Setup.
+ const char expected_data[] = "bleh";
+ int expected_bytes = arraysize(expected_data);
+ ASSERT_EQ(expected_bytes,
+ base::WriteFile(test_path(), expected_data, expected_bytes));
+
+ // Run.
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_OPEN | File::FLAG_READ, &proxy);
+
+ proxy.Read(0, 128, Bind(&FileProxyTest::DidRead, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ EXPECT_EQ(File::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(FileProxyTest, WriteAndFlush) {
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_CREATE | File::FLAG_WRITE, &proxy);
+
+ const char data[] = "foo!";
+ int data_bytes = ARRAYSIZE_UNSAFE(data);
+ proxy.Write(0, data, data_bytes,
+ Bind(&FileProxyTest::DidWrite, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::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.)
+ proxy.Flush(Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::FILE_OK, error_);
+
+ // Verify the written data.
+ char buffer[10];
+ EXPECT_EQ(data_bytes, base::ReadFile(test_path(), buffer, data_bytes));
+ for (int i = 0; i < data_bytes; ++i) {
+ EXPECT_EQ(data[i], buffer[i]);
+ }
+}
+
+TEST_F(FileProxyTest, SetTimes) {
+ FileProxy proxy(file_task_runner());
+ CreateProxy(
+ File::FLAG_CREATE | File::FLAG_WRITE | File::FLAG_WRITE_ATTRIBUTES,
+ &proxy);
+
+ Time last_accessed_time = Time::Now() - TimeDelta::FromDays(12345);
+ Time last_modified_time = Time::Now() - TimeDelta::FromHours(98765);
+
+ proxy.SetTimes(last_accessed_time, last_modified_time,
+ Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(File::FILE_OK, error_);
+
+ File::Info info;
+ 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()));
+}
+
+TEST_F(FileProxyTest, SetLength_Shrink) {
+ // Setup.
+ const char kTestData[] = "0123456789";
+ ASSERT_EQ(10, base::WriteFile(test_path(), kTestData, 10));
+ File::Info info;
+ GetFileInfo(test_path(), &info);
+ ASSERT_EQ(10, info.size);
+
+ // Run.
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
+ proxy.SetLength(7,
+ Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ GetFileInfo(test_path(), &info);
+ ASSERT_EQ(7, info.size);
+
+ char buffer[7];
+ EXPECT_EQ(7, base::ReadFile(test_path(), buffer, 7));
+ int i = 0;
+ for (; i < 7; ++i)
+ EXPECT_EQ(kTestData[i], buffer[i]);
+}
+
+TEST_F(FileProxyTest, SetLength_Expand) {
+ // Setup.
+ const char kTestData[] = "9876543210";
+ ASSERT_EQ(10, base::WriteFile(test_path(), kTestData, 10));
+ File::Info info;
+ GetFileInfo(test_path(), &info);
+ ASSERT_EQ(10, info.size);
+
+ // Run.
+ FileProxy proxy(file_task_runner());
+ CreateProxy(File::FLAG_OPEN | File::FLAG_WRITE, &proxy);
+ proxy.SetLength(53,
+ Bind(&FileProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
+ MessageLoop::current()->Run();
+
+ // Verify.
+ GetFileInfo(test_path(), &info);
+ ASSERT_EQ(53, info.size);
+
+ char buffer[53];
+ EXPECT_EQ(53, base::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/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc
index b2e855da1a0..cba043c332f 100644
--- a/chromium/base/files/file_unittest.cc
+++ b/chromium/base/files/file_unittest.cc
@@ -11,16 +11,27 @@
using base::File;
using base::FilePath;
-TEST(File, Create) {
+TEST(FileTest, Create) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
{
+ // Don't create a File at all.
+ File file;
+ EXPECT_FALSE(file.IsValid());
+ EXPECT_EQ(base::File::FILE_ERROR_FAILED, file.error_details());
+
+ File file2(base::File::FILE_ERROR_TOO_MANY_OPENED);
+ EXPECT_FALSE(file2.IsValid());
+ EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED, file2.error_details());
+ }
+
+ {
// Open a file that doesn't exist.
File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
EXPECT_FALSE(file.IsValid());
- EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, file.error_details());
}
{
@@ -28,7 +39,7 @@ TEST(File, Create) {
File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ);
EXPECT_TRUE(file.IsValid());
EXPECT_TRUE(file.created());
- EXPECT_EQ(base::File::FILE_OK, file.error());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
}
{
@@ -36,7 +47,20 @@ TEST(File, Create) {
File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
EXPECT_TRUE(file.IsValid());
EXPECT_FALSE(file.created());
- EXPECT_EQ(base::File::FILE_OK, file.error());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
+
+ // This time verify closing the file.
+ file.Close();
+ EXPECT_FALSE(file.IsValid());
+ }
+
+ {
+ // Open an existing file through Initialize
+ File file;
+ file.Initialize(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_FALSE(file.created());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
// This time verify closing the file.
file.Close();
@@ -48,16 +72,16 @@ TEST(File, Create) {
File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_READ);
EXPECT_FALSE(file.IsValid());
EXPECT_FALSE(file.created());
- EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error());
+ EXPECT_EQ(base::File::FILE_ERROR_EXISTS, file.error_details());
}
{
// Create or overwrite a file.
File file(file_path,
- base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ);
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
EXPECT_TRUE(file.created());
- EXPECT_EQ(base::File::FILE_OK, file.error());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
}
{
@@ -68,13 +92,31 @@ TEST(File, Create) {
base::File::FLAG_DELETE_ON_CLOSE);
EXPECT_TRUE(file.IsValid());
EXPECT_TRUE(file.created());
- EXPECT_EQ(base::File::FILE_OK, file.error());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
}
EXPECT_FALSE(base::PathExists(file_path));
}
-TEST(File, DeleteOpenFile) {
+TEST(FileTest, Async) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file");
+
+ {
+ File file(file_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_ASYNC);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.async());
+ }
+
+ {
+ File file(file_path, base::File::FLAG_OPEN_ALWAYS);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_FALSE(file.async());
+ }
+}
+
+TEST(FileTest, DeleteOpenFile) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
@@ -85,7 +127,7 @@ TEST(File, DeleteOpenFile) {
base::File::FLAG_SHARE_DELETE);
EXPECT_TRUE(file.IsValid());
EXPECT_TRUE(file.created());
- EXPECT_EQ(base::File::FILE_OK, file.error());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
// Open an existing file and mark it as delete on close.
File same_file(file_path,
@@ -93,7 +135,7 @@ TEST(File, DeleteOpenFile) {
base::File::FLAG_READ);
EXPECT_TRUE(file.IsValid());
EXPECT_FALSE(same_file.created());
- EXPECT_EQ(base::File::FILE_OK, same_file.error());
+ EXPECT_EQ(base::File::FILE_OK, same_file.error_details());
// Close both handles and check that the file is gone.
file.Close();
@@ -101,7 +143,7 @@ TEST(File, DeleteOpenFile) {
EXPECT_FALSE(base::PathExists(file_path));
}
-TEST(File, ReadWrite) {
+TEST(FileTest, ReadWrite) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
@@ -173,7 +215,7 @@ TEST(File, ReadWrite) {
EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
}
-TEST(File, Append) {
+TEST(FileTest, Append) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("append_file");
@@ -221,7 +263,7 @@ TEST(File, Append) {
}
-TEST(File, Truncate) {
+TEST(FileTest, Length) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
@@ -229,6 +271,7 @@ TEST(File, Truncate) {
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
ASSERT_TRUE(file.IsValid());
+ EXPECT_EQ(0, file.GetLength());
// Write "test" to the file.
char data_to_write[] = "test";
@@ -239,7 +282,8 @@ TEST(File, Truncate) {
// Extend the file.
const int kExtendedFileLength = 10;
int64 file_size = 0;
- EXPECT_TRUE(file.Truncate(kExtendedFileLength));
+ EXPECT_TRUE(file.SetLength(kExtendedFileLength));
+ EXPECT_EQ(kExtendedFileLength, file.GetLength());
EXPECT_TRUE(GetFileSize(file_path, &file_size));
EXPECT_EQ(kExtendedFileLength, file_size);
@@ -254,7 +298,8 @@ TEST(File, Truncate) {
// Truncate the file.
const int kTruncatedFileLength = 2;
- EXPECT_TRUE(file.Truncate(kTruncatedFileLength));
+ EXPECT_TRUE(file.SetLength(kTruncatedFileLength));
+ EXPECT_EQ(kTruncatedFileLength, file.GetLength());
EXPECT_TRUE(GetFileSize(file_path, &file_size));
EXPECT_EQ(kTruncatedFileLength, file_size);
@@ -267,9 +312,9 @@ TEST(File, Truncate) {
// Flakily fails: http://crbug.com/86494
#if defined(OS_ANDROID)
-TEST(File, TouchGetInfo) {
+TEST(FileTest, TouchGetInfo) {
#else
-TEST(File, DISABLED_TouchGetInfo) {
+TEST(FileTest, DISABLED_TouchGetInfo) {
#endif
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
@@ -333,18 +378,17 @@ TEST(File, DISABLED_TouchGetInfo) {
creation_time.ToInternalValue());
}
-TEST(File, ReadFileAtCurrentPosition) {
+TEST(FileTest, ReadAtCurrentPosition) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- FilePath file_path =
- temp_dir.path().AppendASCII("read_file_at_current_position");
+ FilePath file_path = temp_dir.path().AppendASCII("read_at_current_position");
File file(file_path,
base::File::FLAG_CREATE | base::File::FLAG_READ |
base::File::FLAG_WRITE);
EXPECT_TRUE(file.IsValid());
const char kData[] = "test";
- const int kDataSize = arraysize(kData) - 1;
+ const int kDataSize = sizeof(kData) - 1;
EXPECT_EQ(kDataSize, file.Write(0, kData, kDataSize));
EXPECT_EQ(0, file.Seek(base::File::FROM_BEGIN, 0));
@@ -355,6 +399,53 @@ TEST(File, ReadFileAtCurrentPosition) {
EXPECT_EQ(kDataSize - first_chunk_size,
file.ReadAtCurrentPos(buffer + first_chunk_size,
kDataSize - first_chunk_size));
- EXPECT_EQ(std::string(buffer, buffer + kDataSize),
- std::string(kData));
+ EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData));
+}
+
+TEST(FileTest, WriteAtCurrentPosition) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("write_at_current_position");
+ File file(file_path,
+ base::File::FLAG_CREATE | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+
+ const char kData[] = "test";
+ const int kDataSize = sizeof(kData) - 1;
+
+ int first_chunk_size = kDataSize / 2;
+ EXPECT_EQ(first_chunk_size, file.WriteAtCurrentPos(kData, first_chunk_size));
+ EXPECT_EQ(kDataSize - first_chunk_size,
+ file.WriteAtCurrentPos(kData + first_chunk_size,
+ kDataSize - first_chunk_size));
+
+ char buffer[kDataSize];
+ EXPECT_EQ(kDataSize, file.Read(0, buffer, kDataSize));
+ EXPECT_EQ(std::string(buffer, buffer + kDataSize), std::string(kData));
+}
+
+#if defined(OS_WIN)
+TEST(FileTest, GetInfoForDirectory) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath empty_dir = temp_dir.path().Append(FILE_PATH_LITERAL("gpfi_test"));
+ ASSERT_TRUE(CreateDirectory(empty_dir));
+
+ base::File 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::File::Info info;
+ EXPECT_TRUE(dir.GetInfo(&info));
+ EXPECT_TRUE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+ EXPECT_EQ(0, info.size);
}
+#endif // defined(OS_WIN)
diff --git a/chromium/base/files/file_util_proxy.cc b/chromium/base/files/file_util_proxy.cc
index 40cac112820..72d9436fa1f 100644
--- a/chromium/base/files/file_util_proxy.cc
+++ b/chromium/base/files/file_util_proxy.cc
@@ -8,7 +8,6 @@
#include "base/bind_helpers.h"
#include "base/file_util.h"
#include "base/location.h"
-#include "base/message_loop/message_loop_proxy.h"
#include "base/task_runner.h"
#include "base/task_runner_util.h"
@@ -19,105 +18,21 @@ namespace {
void CallWithTranslatedParameter(const FileUtilProxy::StatusCallback& callback,
bool value) {
DCHECK(!callback.is_null());
- callback.Run(value ? PLATFORM_FILE_OK : PLATFORM_FILE_ERROR_FAILED);
+ callback.Run(value ? File::FILE_OK : File::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) {
- DCHECK(!callback.is_null());
- callback.Run(error_, PassPlatformFile(&file_handle_), created_);
- }
-
- 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_.get(), 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.
- base::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) {}
+ : error_(File::FILE_OK) {}
void RunWorkForFilePath(const FilePath& file_path) {
if (!PathExists(file_path)) {
- error_ = PLATFORM_FILE_ERROR_NOT_FOUND;
+ error_ = File::FILE_ERROR_NOT_FOUND;
return;
}
if (!GetFileInfo(file_path, &file_info_))
- error_ = PLATFORM_FILE_ERROR_FAILED;
- }
-
- void RunWorkForPlatformFile(PlatformFile file) {
- if (!GetPlatformFileInfo(file, &file_info_))
- error_ = PLATFORM_FILE_ERROR_FAILED;
+ error_ = File::FILE_ERROR_FAILED;
}
void Reply(const FileUtilProxy::GetFileInfoCallback& callback) {
@@ -127,138 +42,26 @@ class GetFileInfoHelper {
}
private:
- PlatformFileError error_;
- PlatformFileInfo file_info_;
+ File::Error error_;
+ File::Info 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_ptr<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_ptr<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 (!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) {
+File::Error DeleteAdapter(const FilePath& file_path, bool recursive) {
if (!PathExists(file_path)) {
- return PLATFORM_FILE_ERROR_NOT_FOUND;
+ return File::FILE_ERROR_NOT_FOUND;
}
if (!base::DeleteFile(file_path, recursive)) {
if (!recursive && !base::IsDirectoryEmpty(file_path)) {
- return PLATFORM_FILE_ERROR_NOT_EMPTY;
+ return File::FILE_ERROR_NOT_EMPTY;
}
- return PLATFORM_FILE_ERROR_FAILED;
+ return File::FILE_ERROR_FAILED;
}
- return PLATFORM_FILE_OK;
+ return File::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(
@@ -274,19 +77,6 @@ bool FileUtilProxy::GetFileInfo(
}
// 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::DeleteFile(TaskRunner* task_runner,
const FilePath& file_path,
bool recursive,
@@ -298,56 +88,6 @@ bool FileUtilProxy::DeleteFile(TaskRunner* task_runner,
}
// 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));
-}
-
-// 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,
@@ -361,53 +101,4 @@ bool FileUtilProxy::Touch(
Bind(&CallWithTranslatedParameter, callback));
}
-// 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::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));
-}
-
-// 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/chromium/base/files/file_util_proxy.h b/chromium/base/files/file_util_proxy.h
index bded1612005..80688cfbb48 100644
--- a/chromium/base/files/file_util_proxy.h
+++ b/chromium/base/files/file_util_proxy.h
@@ -7,13 +7,8 @@
#include "base/base_export.h"
#include "base/callback_forward.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
-#include "base/memory/ref_counted.h"
-#include "base/platform_file.h"
-
-namespace tracked_objects {
-class Location;
-};
namespace base {
@@ -26,57 +21,10 @@ class BASE_EXPORT FileUtilProxy {
// 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<void(File::Error)> StatusCallback;
- 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.
- //
- // This returns false if task posting to |task_runner| has failed.
- 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.
- //
- // This returns false if task posting to |task_runner| has failed.
- static bool CreateTemporary(
- TaskRunner* task_runner,
- int additional_file_flags,
- const CreateTemporaryCallback& callback);
-
- // Close the given file handle.
- // This returns false if task posting to |task_runner| has failed.
- static bool Close(TaskRunner* task_runner,
- PlatformFile,
- const StatusCallback& callback);
+ typedef Callback<void(File::Error,
+ const File::Info&)> GetFileInfoCallback;
// Retrieves the information about a file. It is invalid to pass a null
// callback.
@@ -86,13 +34,6 @@ class BASE_EXPORT FileUtilProxy {
const FilePath& file_path,
const GetFileInfoCallback& callback);
- // Does the same as GetFileInfo but takes PlatformFile instead of FilePath.
- // This returns false if task posting to |task_runner| has failed.
- 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.
// This returns false if task posting to |task_runner| has failed.
@@ -101,42 +42,6 @@ class BASE_EXPORT FileUtilProxy {
bool recursive,
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.
- //
- // This returns false if |bytes_to_read| is less than zero, or
- // if task posting to |task_runner| has failed.
- 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.
- //
- // This returns false if |bytes_to_write| is less than or equal to zero,
- // if |buffer| is NULL, or if task posting to |task_runner| has failed.
- static bool Write(
- TaskRunner* task_runner,
- PlatformFile file,
- int64 offset,
- const char* buffer,
- int bytes_to_write,
- const WriteCallback& callback);
-
- // Touches a file. The callback can be null.
- // This returns false if task posting to |task_runner| has failed.
- 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.
// This returns false if task posting to |task_runner| has failed.
static bool Touch(
@@ -146,46 +51,6 @@ class BASE_EXPORT FileUtilProxy {
const Time& last_modified_time,
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.
- // This returns false if task posting to |task_runner| has failed.
- 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.
- // This returns false if task posting to |task_runner| has failed.
- static bool Truncate(
- TaskRunner* task_runner,
- const FilePath& path,
- int64 length,
- const StatusCallback& callback);
-
- // Flushes a file. The callback can be null.
- // This returns false if task posting to |task_runner| has failed.
- static bool Flush(
- TaskRunner* task_runner,
- PlatformFile file,
- const StatusCallback& callback);
-
- // Relay helpers.
- // They return false if posting a given task to |task_runner| has failed.
- 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);
};
diff --git a/chromium/base/files/file_util_proxy_unittest.cc b/chromium/base/files/file_util_proxy_unittest.cc
index 17c7a3f7cf5..52073ae3b50 100644
--- a/chromium/base/files/file_util_proxy_unittest.cc
+++ b/chromium/base/files/file_util_proxy_unittest.cc
@@ -4,15 +4,11 @@
#include "base/files/file_util_proxy.h"
-#include <map>
-
#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
-#include "base/platform_file.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -21,11 +17,9 @@ namespace base {
class FileUtilProxyTest : public testing::Test {
public:
FileUtilProxyTest()
- : message_loop_(MessageLoop::TYPE_IO),
- file_thread_("FileUtilProxyTestFileThread"),
- error_(PLATFORM_FILE_OK),
+ : file_thread_("FileUtilProxyTestFileThread"),
+ error_(File::FILE_OK),
created_(false),
- file_(kInvalidPlatformFileValue),
bytes_written_(-1),
weak_factory_(this) {}
@@ -34,198 +28,43 @@ class FileUtilProxyTest : public testing::Test {
ASSERT_TRUE(file_thread_.Start());
}
- virtual void TearDown() OVERRIDE {
- if (file_ != kInvalidPlatformFileValue)
- ClosePlatformFile(file_);
- }
-
- void DidFinish(PlatformFileError error) {
- error_ = error;
- MessageLoop::current()->QuitWhenIdle();
- }
-
- void DidCreateOrOpen(PlatformFileError error,
- PassPlatformFile file,
- bool created) {
+ void DidFinish(File::Error error) {
error_ = error;
- file_ = file.ReleaseValue();
- created_ = created;
MessageLoop::current()->QuitWhenIdle();
}
- void DidCreateTemporary(PlatformFileError error,
- PassPlatformFile file,
- const FilePath& path) {
- error_ = error;
- file_ = file.ReleaseValue();
- path_ = path;
- MessageLoop::current()->QuitWhenIdle();
- }
-
- void DidGetFileInfo(PlatformFileError error,
- const PlatformFileInfo& file_info) {
+ void DidGetFileInfo(File::Error error,
+ const File::Info& file_info) {
error_ = error;
file_info_ = file_info;
MessageLoop::current()->QuitWhenIdle();
}
- void DidRead(PlatformFileError error,
- const char* data,
- int bytes_read) {
- error_ = error;
- buffer_.resize(bytes_read);
- memcpy(&buffer_[0], data, bytes_read);
- MessageLoop::current()->QuitWhenIdle();
- }
-
- void DidWrite(PlatformFileError error,
- int bytes_written) {
- error_ = error;
- bytes_written_ = bytes_written;
- MessageLoop::current()->QuitWhenIdle();
- }
-
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_;
+ MessageLoopForIO message_loop_;
Thread file_thread_;
ScopedTempDir dir_;
- PlatformFileError error_;
+ File::Error error_;
bool created_;
- PlatformFile file_;
FilePath path_;
- PlatformFileInfo file_info_;
+ File::Info 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(PathExists(test_path()));
-}
-
-TEST_F(FileUtilProxyTest, CreateOrOpen_Open) {
- // Creates a file.
- file_util::WriteFile(test_path(), NULL, 0);
- ASSERT_TRUE(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(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(base::Move(test_path(),
- test_dir_path().AppendASCII("new")));
-#endif
-
- FileUtilProxy::Close(
- file_task_runner(),
- file,
- Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
- MessageLoop::current()->Run();
- EXPECT_EQ(PLATFORM_FILE_OK, error_);
-
- // Now it should pass on all platforms.
- EXPECT_TRUE(base::Move(test_path(), test_dir_path().AppendASCII("new")));
-}
-
-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(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(ReadFileToString(path_, &data));
- EXPECT_EQ("test", data);
-
- // Make sure we can & do delete the created file to prevent leaks on the bots.
- EXPECT_TRUE(base::DeleteFile(path_, false));
-}
TEST_F(FileUtilProxyTest, GetFileInfo_File) {
// Setup.
- ASSERT_EQ(4, file_util::WriteFile(test_path(), "test", 4));
- PlatformFileInfo expected_info;
+ ASSERT_EQ(4, WriteFile(test_path(), "test", 4));
+ File::Info expected_info;
GetFileInfo(test_path(), &expected_info);
// Run.
@@ -236,7 +75,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) {
MessageLoop::current()->Run();
// Verify.
- EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(File::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);
@@ -248,7 +87,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) {
TEST_F(FileUtilProxyTest, GetFileInfo_Directory) {
// Setup.
ASSERT_TRUE(base::CreateDirectory(test_path()));
- PlatformFileInfo expected_info;
+ File::Info expected_info;
GetFileInfo(test_path(), &expected_info);
// Run.
@@ -259,7 +98,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_Directory) {
MessageLoop::current()->Run();
// Verify.
- EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(File::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);
@@ -268,80 +107,21 @@ TEST_F(FileUtilProxyTest, GetFileInfo_Directory) {
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_);
-
- // Verify the written data.
- char buffer[10];
- EXPECT_EQ(data_bytes, base::ReadFile(test_path(), buffer, data_bytes));
- for (int i = 0; i < data_bytes; ++i) {
- EXPECT_EQ(data[i], buffer[i]);
- }
-}
-
TEST_F(FileUtilProxyTest, Touch) {
+ ASSERT_EQ(4, WriteFile(test_path(), "test", 4));
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),
+ test_path(),
last_accessed_time,
last_modified_time,
Bind(&FileUtilProxyTest::DidFinish, weak_factory_.GetWeakPtr()));
MessageLoop::current()->Run();
- EXPECT_EQ(PLATFORM_FILE_OK, error_);
+ EXPECT_EQ(File::FILE_OK, error_);
- PlatformFileInfo info;
+ File::Info info;
GetFileInfo(test_path(), &info);
// The returned values may only have the seconds precision, so we cast
@@ -352,60 +132,4 @@ TEST_F(FileUtilProxyTest, Touch) {
static_cast<int>(info.last_accessed.ToDoubleT()));
}
-TEST_F(FileUtilProxyTest, Truncate_Shrink) {
- // Setup.
- const char kTestData[] = "0123456789";
- ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10));
- PlatformFileInfo info;
- 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.
- GetFileInfo(test_path(), &info);
- ASSERT_EQ(7, info.size);
-
- char buffer[7];
- EXPECT_EQ(7, base::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;
- 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.
- GetFileInfo(test_path(), &info);
- ASSERT_EQ(53, info.size);
-
- char buffer[53];
- EXPECT_EQ(53, base::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/chromium/base/files/file_win.cc b/chromium/base/files/file_win.cc
index 94f4d7f59c9..9e18bda70b1 100644
--- a/chromium/base/files/file_win.cc
+++ b/chromium/base/files/file_win.cc
@@ -13,7 +13,7 @@
namespace base {
-void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
+void File::InitializeUnsafe(const FilePath& name, uint32 flags) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(!IsValid());
@@ -34,6 +34,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
if (flags & FLAG_CREATE_ALWAYS) {
DCHECK(!disposition);
+ DCHECK(flags & FLAG_WRITE);
disposition = CREATE_ALWAYS;
}
@@ -84,7 +85,7 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
disposition, create_flags, NULL));
if (file_.IsValid()) {
- error_ = FILE_OK;
+ error_details_ = FILE_OK;
async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
if (flags & (FLAG_OPEN_ALWAYS))
@@ -92,20 +93,27 @@ void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
created_ = true;
} else {
- error_ = OSErrorToFileError(GetLastError());
+ error_details_ = OSErrorToFileError(GetLastError());
}
}
bool File::IsValid() const {
return file_.IsValid();
}
+
+PlatformFile File::GetPlatformFile() const {
+ return file_;
+}
+
PlatformFile File::TakePlatformFile() {
return file_.Take();
}
void File::Close() {
- base::ThreadRestrictions::AssertIOAllowed();
- file_.Close();
+ if (file_.IsValid()) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ file_.Close();
+ }
}
int64 File::Seek(Whence whence, int64 offset) {
@@ -137,7 +145,7 @@ int File::Read(int64 offset, char* data, int size) {
overlapped.OffsetHigh = offset_li.HighPart;
DWORD bytes_read;
- if (::ReadFile(file_, data, size, &bytes_read, &overlapped) != 0)
+ if (::ReadFile(file_, data, size, &bytes_read, &overlapped))
return bytes_read;
if (ERROR_HANDLE_EOF == GetLastError())
return 0;
@@ -153,7 +161,7 @@ int File::ReadAtCurrentPos(char* data, int size) {
return -1;
DWORD bytes_read;
- if (::ReadFile(file_, data, size, &bytes_read, NULL) != 0)
+ if (::ReadFile(file_, data, size, &bytes_read, NULL))
return bytes_read;
if (ERROR_HANDLE_EOF == GetLastError())
return 0;
@@ -182,14 +190,23 @@ int File::Write(int64 offset, const char* data, int size) {
overlapped.OffsetHigh = offset_li.HighPart;
DWORD bytes_written;
- if (::WriteFile(file_, data, size, &bytes_written, &overlapped) != 0)
+ if (::WriteFile(file_, data, size, &bytes_written, &overlapped))
return bytes_written;
return -1;
}
int File::WriteAtCurrentPos(const char* data, int size) {
- NOTREACHED();
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ DCHECK(!async_);
+ if (size < 0)
+ return -1;
+
+ DWORD bytes_written;
+ if (::WriteFile(file_, data, size, &bytes_written, NULL))
+ return bytes_written;
+
return -1;
}
@@ -197,7 +214,17 @@ int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
return WriteAtCurrentPos(data, size);
}
-bool File::Truncate(int64 length) {
+int64 File::GetLength() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ LARGE_INTEGER size;
+ if (!::GetFileSizeEx(file_.Get(), &size))
+ return -1;
+
+ return static_cast<int64>(size.QuadPart);
+}
+
+bool File::SetLength(int64 length) {
base::ThreadRestrictions::AssertIOAllowed();
DCHECK(IsValid());
@@ -205,7 +232,7 @@ bool File::Truncate(int64 length) {
LARGE_INTEGER file_pointer;
LARGE_INTEGER zero;
zero.QuadPart = 0;
- if (::SetFilePointerEx(file_, zero, &file_pointer, FILE_CURRENT) == 0)
+ if (!::SetFilePointerEx(file_, zero, &file_pointer, FILE_CURRENT))
return false;
LARGE_INTEGER length_li;
@@ -218,8 +245,11 @@ bool File::Truncate(int64 length) {
// Set the new file length and move the file pointer to its old position.
// This is consistent with ftruncate()'s behavior, even when the file
// pointer points to a location beyond the end of the file.
- return ((::SetEndOfFile(file_) != 0) &&
- (::SetFilePointerEx(file_, file_pointer, NULL, FILE_BEGIN) != 0));
+ // TODO(rvargas): Emulating ftruncate details seem suspicious and it is not
+ // promised by the interface (nor was promised by PlatformFile). See if this
+ // implementation detail can be removed.
+ return ((::SetEndOfFile(file_) != FALSE) &&
+ (::SetFilePointerEx(file_, file_pointer, NULL, FILE_BEGIN) != FALSE));
}
bool File::Flush() {
@@ -235,7 +265,7 @@ bool File::SetTimes(Time last_access_time, Time last_modified_time) {
FILETIME last_access_filetime = last_access_time.ToFileTime();
FILETIME last_modified_filetime = last_modified_time.ToFileTime();
return (::SetFileTime(file_, NULL, &last_access_filetime,
- &last_modified_filetime) != 0);
+ &last_modified_filetime) != FALSE);
}
bool File::GetInfo(Info* info) {
@@ -243,7 +273,7 @@ bool File::GetInfo(Info* info) {
DCHECK(IsValid());
BY_HANDLE_FILE_INFORMATION file_info;
- if (GetFileInformationByHandle(file_, &file_info) == 0)
+ if (!GetFileInformationByHandle(file_, &file_info))
return false;
LARGE_INTEGER size;
diff --git a/chromium/base/files/important_file_writer.cc b/chromium/base/files/important_file_writer.cc
index 261c98772e5..bf4e0033e3a 100644
--- a/chromium/base/files/important_file_writer.cc
+++ b/chromium/base/files/important_file_writer.cc
@@ -2,12 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#if defined _MSC_VER && _MSC_VER == 1800
-// TODO(scottmg): Internal errors on VS2013 RC in LTCG. This should be removed
-// after RTM. http://crbug.com/288948
-#pragma optimize("", off)
-#endif
-
#include "base/files/important_file_writer.h"
#include <stdio.h>
@@ -17,11 +11,13 @@
#include "base/bind.h"
#include "base/critical_closure.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h"
#include "base/task_runner.h"
+#include "base/task_runner_util.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
@@ -63,25 +59,18 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
return false;
}
- int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE;
- PlatformFile tmp_file =
- CreatePlatformFile(tmp_file_path, flags, NULL, NULL);
- if (tmp_file == kInvalidPlatformFileValue) {
+ File tmp_file(tmp_file_path, File::FLAG_OPEN | File::FLAG_WRITE);
+ if (!tmp_file.IsValid()) {
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");
- base::DeleteFile(tmp_file_path, false);
- return false;
- }
+ int bytes_written = tmp_file.Write(0, data.data(),
+ static_cast<int>(data.length()));
+ tmp_file.Flush(); // Ignore return value.
+ tmp_file.Close();
if (bytes_written < static_cast<int>(data.length())) {
LogFailure(path, FAILED_WRITING, "error writing, bytes_written=" +
@@ -99,13 +88,13 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
return true;
}
-ImportantFileWriter::ImportantFileWriter(
- const FilePath& path, base::SequencedTaskRunner* task_runner)
- : path_(path),
- task_runner_(task_runner),
- serializer_(NULL),
- commit_interval_(TimeDelta::FromMilliseconds(
- kDefaultCommitIntervalMs)) {
+ImportantFileWriter::ImportantFileWriter(const FilePath& path,
+ base::SequencedTaskRunner* task_runner)
+ : path_(path),
+ task_runner_(task_runner),
+ serializer_(NULL),
+ commit_interval_(TimeDelta::FromMilliseconds(kDefaultCommitIntervalMs)),
+ weak_factory_(this) {
DCHECK(CalledOnValidThread());
DCHECK(task_runner_.get());
}
@@ -132,11 +121,7 @@ void ImportantFileWriter::WriteNow(const std::string& data) {
if (HasPendingWrite())
timer_.Stop();
- if (!task_runner_->PostTask(
- FROM_HERE,
- MakeCriticalClosure(
- Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
- path_, data)))) {
+ if (!PostWriteTask(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.
@@ -170,4 +155,40 @@ void ImportantFileWriter::DoScheduledWrite() {
serializer_ = NULL;
}
+void ImportantFileWriter::RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write) {
+ DCHECK(on_next_successful_write_.is_null());
+ on_next_successful_write_ = on_next_successful_write;
+}
+
+bool ImportantFileWriter::PostWriteTask(const std::string& data) {
+ // TODO(gab): This code could always use PostTaskAndReplyWithResult and let
+ // ForwardSuccessfulWrite() no-op if |on_next_successful_write_| is null, but
+ // PostTaskAndReply causes memory leaks in tests (crbug.com/371974) and
+ // suppressing all of those is unrealistic hence we avoid most of them by
+ // using PostTask() in the typical scenario below.
+ if (!on_next_successful_write_.is_null()) {
+ return base::PostTaskAndReplyWithResult(
+ task_runner_,
+ FROM_HERE,
+ MakeCriticalClosure(
+ Bind(&ImportantFileWriter::WriteFileAtomically, path_, data)),
+ Bind(&ImportantFileWriter::ForwardSuccessfulWrite,
+ weak_factory_.GetWeakPtr()));
+ }
+ return task_runner_->PostTask(
+ FROM_HERE,
+ MakeCriticalClosure(
+ Bind(IgnoreResult(&ImportantFileWriter::WriteFileAtomically),
+ path_, data)));
+}
+
+void ImportantFileWriter::ForwardSuccessfulWrite(bool result) {
+ DCHECK(CalledOnValidThread());
+ if (result && !on_next_successful_write_.is_null()) {
+ on_next_successful_write_.Run();
+ on_next_successful_write_.Reset();
+ }
+}
+
} // namespace base
diff --git a/chromium/base/files/important_file_writer.h b/chromium/base/files/important_file_writer.h
index ba1c745a4ef..61a53b16503 100644
--- a/chromium/base/files/important_file_writer.h
+++ b/chromium/base/files/important_file_writer.h
@@ -9,6 +9,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/threading/non_thread_safe.h"
@@ -89,6 +90,11 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
// Serialize data pending to be saved and execute write on backend thread.
void DoScheduledWrite();
+ // Registers |on_next_successful_write| to be called once, on the next
+ // successful write event. Only one callback can be set at once.
+ void RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write);
+
TimeDelta commit_interval() const {
return commit_interval_;
}
@@ -98,6 +104,16 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
}
private:
+ // Helper method for WriteNow().
+ bool PostWriteTask(const std::string& data);
+
+ // If |result| is true and |on_next_successful_write_| is set, invokes
+ // |on_successful_write_| and then resets it; no-ops otherwise.
+ void ForwardSuccessfulWrite(bool result);
+
+ // Invoked once and then reset on the next successful write event.
+ base::Closure on_next_successful_write_;
+
// Path being written to.
const FilePath path_;
@@ -113,6 +129,8 @@ class BASE_EXPORT ImportantFileWriter : public NonThreadSafe {
// Time delta after which scheduled data will be written to disk.
TimeDelta commit_interval_;
+ WeakPtrFactory<ImportantFileWriter> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(ImportantFileWriter);
};
diff --git a/chromium/base/files/important_file_writer_unittest.cc b/chromium/base/files/important_file_writer_unittest.cc
index 02a5f76b2b6..3f62fe4953b 100644
--- a/chromium/base/files/important_file_writer_unittest.cc
+++ b/chromium/base/files/important_file_writer_unittest.cc
@@ -4,6 +4,7 @@
#include "base/files/important_file_writer.h"
+#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
@@ -41,6 +42,41 @@ class DataSerializer : public ImportantFileWriter::DataSerializer {
const std::string data_;
};
+class SuccessfulWriteObserver {
+ public:
+ SuccessfulWriteObserver() : successful_write_observed_(false) {}
+
+ // Register on_successful_write() to be called on the next successful write
+ // of |writer|.
+ void ObserveNextSuccessfulWrite(ImportantFileWriter* writer);
+
+ // Returns true if a successful write was observed via on_successful_write()
+ // and resets the observation state to false regardless.
+ bool GetAndResetObservationState();
+
+ private:
+ void on_successful_write() {
+ EXPECT_FALSE(successful_write_observed_);
+ successful_write_observed_ = true;
+ }
+
+ bool successful_write_observed_;
+
+ DISALLOW_COPY_AND_ASSIGN(SuccessfulWriteObserver);
+};
+
+void SuccessfulWriteObserver::ObserveNextSuccessfulWrite(
+ ImportantFileWriter* writer) {
+ writer->RegisterOnNextSuccessfulWriteCallback(base::Bind(
+ &SuccessfulWriteObserver::on_successful_write, base::Unretained(this)));
+}
+
+bool SuccessfulWriteObserver::GetAndResetObservationState() {
+ bool was_successful_write_observed = successful_write_observed_;
+ successful_write_observed_ = false;
+ return was_successful_write_observed;
+}
+
} // namespace
class ImportantFileWriterTest : public testing::Test {
@@ -52,6 +88,7 @@ class ImportantFileWriterTest : public testing::Test {
}
protected:
+ SuccessfulWriteObserver successful_write_observer_;
FilePath file_;
MessageLoop loop_;
@@ -62,11 +99,47 @@ class ImportantFileWriterTest : public testing::Test {
TEST_F(ImportantFileWriterTest, Basic) {
ImportantFileWriter writer(file_, MessageLoopProxy::current().get());
EXPECT_FALSE(PathExists(writer.path()));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ writer.WriteNow("foo");
+ RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ ASSERT_TRUE(PathExists(writer.path()));
+ EXPECT_EQ("foo", GetFileContent(writer.path()));
+}
+
+TEST_F(ImportantFileWriterTest, BasicWithSuccessfulWriteObserver) {
+ ImportantFileWriter writer(file_, MessageLoopProxy::current().get());
+ EXPECT_FALSE(PathExists(writer.path()));
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ successful_write_observer_.ObserveNextSuccessfulWrite(&writer);
writer.WriteNow("foo");
RunLoop().RunUntilIdle();
+ // Confirm that the observer is invoked.
+ EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState());
ASSERT_TRUE(PathExists(writer.path()));
EXPECT_EQ("foo", GetFileContent(writer.path()));
+
+ // Confirm that re-installing the observer works for another write.
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ successful_write_observer_.ObserveNextSuccessfulWrite(&writer);
+ writer.WriteNow("bar");
+ RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(successful_write_observer_.GetAndResetObservationState());
+ ASSERT_TRUE(PathExists(writer.path()));
+ EXPECT_EQ("bar", GetFileContent(writer.path()));
+
+ // Confirm that writing again without re-installing the observer doesn't
+ // result in a notification.
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ writer.WriteNow("baz");
+ RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(successful_write_observer_.GetAndResetObservationState());
+ ASSERT_TRUE(PathExists(writer.path()));
+ EXPECT_EQ("baz", GetFileContent(writer.path()));
}
TEST_F(ImportantFileWriterTest, ScheduleWrite) {
diff --git a/chromium/base/files/memory_mapped_file.cc b/chromium/base/files/memory_mapped_file.cc
index a48ec0ceb2a..ace4e112628 100644
--- a/chromium/base/files/memory_mapped_file.cc
+++ b/chromium/base/files/memory_mapped_file.cc
@@ -17,7 +17,14 @@ bool MemoryMappedFile::Initialize(const FilePath& file_name) {
if (IsValid())
return false;
- if (!MapFileToMemory(file_name)) {
+ file_.Initialize(file_name, File::FLAG_OPEN | File::FLAG_READ);
+
+ if (!file_.IsValid()) {
+ DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
+ return false;
+ }
+
+ if (!MapFileToMemory()) {
CloseHandles();
return false;
}
@@ -25,13 +32,13 @@ bool MemoryMappedFile::Initialize(const FilePath& file_name) {
return true;
}
-bool MemoryMappedFile::Initialize(PlatformFile file) {
+bool MemoryMappedFile::Initialize(File file) {
if (IsValid())
return false;
- file_ = file;
+ file_ = file.Pass();
- if (!MapFileToMemoryInternal()) {
+ if (!MapFileToMemory()) {
CloseHandles();
return false;
}
@@ -43,16 +50,4 @@ bool MemoryMappedFile::IsValid() const {
return data_ != NULL;
}
-bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) {
- file_ = CreatePlatformFile(file_name, PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
- NULL, NULL);
-
- if (file_ == kInvalidPlatformFileValue) {
- DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
- return false;
- }
-
- return MapFileToMemoryInternal();
-}
-
} // namespace base
diff --git a/chromium/base/files/memory_mapped_file.h b/chromium/base/files/memory_mapped_file.h
index 6df1bad6359..b02d8cfbdae 100644
--- a/chromium/base/files/memory_mapped_file.h
+++ b/chromium/base/files/memory_mapped_file.h
@@ -7,7 +7,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
-#include "base/platform_file.h"
+#include "base/files/file.h"
#include "build/build_config.h"
#if defined(OS_WIN)
@@ -30,9 +30,10 @@ class BASE_EXPORT MemoryMappedFile {
// 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(PlatformFile file);
+
+ // As above, but works with an already-opened file. MemoryMappedFile takes
+ // ownership of |file| and closes it when done.
+ bool Initialize(File file);
#if defined(OS_WIN)
// Opens an existing file and maps it as an image section. Please refer to
@@ -47,27 +48,22 @@ class BASE_EXPORT MemoryMappedFile {
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();
+ bool MapFileToMemory();
- // Closes all open handles. Later we may want to make this public.
+ // Closes all open handles.
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
- PlatformFile file_;
+ File file_;
uint8* data_;
size_t length_;
+#if defined(OS_WIN)
+ win::ScopedHandle file_mapping_;
+ bool image_; // Map as an image.
+#endif
+
DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
};
diff --git a/chromium/base/files/memory_mapped_file_posix.cc b/chromium/base/files/memory_mapped_file_posix.cc
index c4c477a3fe7..5d7e0079992 100644
--- a/chromium/base/files/memory_mapped_file_posix.cc
+++ b/chromium/base/files/memory_mapped_file_posix.cc
@@ -13,26 +13,23 @@
namespace base {
-MemoryMappedFile::MemoryMappedFile()
- : file_(kInvalidPlatformFileValue),
- data_(NULL),
- length_(0) {
+MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) {
}
-bool MemoryMappedFile::MapFileToMemoryInternal() {
+bool MemoryMappedFile::MapFileToMemory() {
ThreadRestrictions::AssertIOAllowed();
struct stat file_stat;
- if (fstat(file_, &file_stat) == kInvalidPlatformFileValue) {
- DPLOG(ERROR) << "fstat " << file_;
+ if (fstat(file_.GetPlatformFile(), &file_stat) == -1 ) {
+ DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
return false;
}
length_ = file_stat.st_size;
data_ = static_cast<uint8*>(
- mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0));
+ mmap(NULL, length_, PROT_READ, MAP_SHARED, file_.GetPlatformFile(), 0));
if (data_ == MAP_FAILED)
- DPLOG(ERROR) << "mmap " << file_;
+ DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
return data_ != MAP_FAILED;
}
@@ -42,12 +39,10 @@ void MemoryMappedFile::CloseHandles() {
if (data_ != NULL)
munmap(data_, length_);
- if (file_ != kInvalidPlatformFileValue)
- close(file_);
+ file_.Close();
data_ = NULL;
length_ = 0;
- file_ = kInvalidPlatformFileValue;
}
} // namespace base
diff --git a/chromium/base/files/memory_mapped_file_win.cc b/chromium/base/files/memory_mapped_file_win.cc
index 694212950de..f3822873bfd 100644
--- a/chromium/base/files/memory_mapped_file_win.cc
+++ b/chromium/base/files/memory_mapped_file_win.cc
@@ -5,83 +5,52 @@
#include "base/files/memory_mapped_file.h"
#include "base/files/file_path.h"
-#include "base/logging.h"
-#include "base/metrics/histogram.h"
#include "base/strings/string16.h"
#include "base/threading/thread_restrictions.h"
namespace base {
-MemoryMappedFile::MemoryMappedFile()
- : file_(INVALID_HANDLE_VALUE),
- file_mapping_(INVALID_HANDLE_VALUE),
- data_(NULL),
- length_(INVALID_FILE_SIZE) {
+MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0), image_(false) {
}
bool MemoryMappedFile::InitializeAsImageSection(const FilePath& file_name) {
- if (IsValid())
- return false;
- file_ = CreatePlatformFile(file_name, PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
- NULL, NULL);
-
- if (file_ == kInvalidPlatformFileValue) {
- DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
- return false;
- }
-
- if (!MapFileToMemoryInternalEx(SEC_IMAGE)) {
- CloseHandles();
- return false;
- }
-
- return true;
-}
-
-bool MemoryMappedFile::MapFileToMemoryInternal() {
- return MapFileToMemoryInternalEx(0);
+ image_ = true;
+ return Initialize(file_name);
}
-bool MemoryMappedFile::MapFileToMemoryInternalEx(int flags) {
+bool MemoryMappedFile::MapFileToMemory() {
ThreadRestrictions::AssertIOAllowed();
- if (file_ == INVALID_HANDLE_VALUE)
+ if (!file_.IsValid())
return false;
- length_ = ::GetFileSize(file_, NULL);
- if (length_ == INVALID_FILE_SIZE)
+ int64 len = file_.GetLength();
+ if (len <= 0 || len > kint32max)
return false;
+ length_ = static_cast<size_t>(len);
+
+ int flags = image_ ? SEC_IMAGE | PAGE_READONLY : PAGE_READONLY;
- 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);
+ file_mapping_.Set(::CreateFileMapping(file_.GetPlatformFile(), NULL,
+ flags, 0, 0, NULL));
+ if (!file_mapping_.IsValid())
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);
- }
+ data_ = static_cast<uint8*>(::MapViewOfFile(file_mapping_.Get(),
+ FILE_MAP_READ, 0, 0, 0));
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_);
+ if (file_mapping_.IsValid())
+ file_mapping_.Close();
+ if (file_.IsValid())
+ file_.Close();
data_ = NULL;
- file_mapping_ = file_ = INVALID_HANDLE_VALUE;
- length_ = INVALID_FILE_SIZE;
+ length_ = 0;
}
} // namespace base
diff --git a/chromium/base/files/scoped_file.cc b/chromium/base/files/scoped_file.cc
new file mode 100644
index 00000000000..39f064de1c4
--- /dev/null
+++ b/chromium/base/files/scoped_file.cc
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All 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_file.h"
+
+#include "base/logging.h"
+
+#if defined(OS_POSIX)
+#include <unistd.h>
+
+#include "base/posix/eintr_wrapper.h"
+#endif
+
+namespace base {
+namespace internal {
+
+#if defined(OS_POSIX)
+
+// static
+void ScopedFDCloseTraits::Free(int fd) {
+ // It's important to crash here.
+ // There are security implications to not closing a file descriptor
+ // properly. As file descriptors are "capabilities", keeping them open
+ // would make the current process keep access to a resource. Much of
+ // Chrome relies on being able to "drop" such access.
+ // It's especially problematic on Linux with the setuid sandbox, where
+ // a single open directory would bypass the entire security model.
+ PCHECK(0 == IGNORE_EINTR(close(fd)));
+}
+
+#endif // OS_POSIX
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/files/scoped_file.h b/chromium/base/files/scoped_file.h
new file mode 100644
index 00000000000..106f6ad94bf
--- /dev/null
+++ b/chromium/base/files/scoped_file.h
@@ -0,0 +1,61 @@
+// Copyright 2014 The Chromium Authors. All 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_FILE_H_
+#define BASE_FILES_SCOPED_FILE_H_
+
+#include <stdio.h>
+
+#include "base/base_export.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/scoped_generic.h"
+#include "build/build_config.h"
+
+namespace base {
+
+namespace internal {
+
+#if defined(OS_POSIX)
+struct BASE_EXPORT ScopedFDCloseTraits {
+ static int InvalidValue() {
+ return -1;
+ }
+ static void Free(int fd);
+};
+#endif
+
+// Functor for |ScopedFILE| (below).
+struct ScopedFILECloser {
+ inline void operator()(FILE* x) const {
+ if (x)
+ fclose(x);
+ }
+};
+
+} // namespace internal
+
+// -----------------------------------------------------------------------------
+
+#if defined(OS_POSIX)
+// A low-level Posix file descriptor closer class. Use this when writing
+// platform-specific code, especially that does non-file-like things with the
+// FD (like sockets).
+//
+// If you're writing low-level Windows code, see base/win/scoped_handle.h
+// which provides some additional functionality.
+//
+// If you're writing cross-platform code that deals with actual files, you
+// should generally use base::File instead which can be constructed with a
+// handle, and in addition to handling ownership, has convenient cross-platform
+// file manipulation functions on it.
+typedef ScopedGeneric<int, internal::ScopedFDCloseTraits> ScopedFD;
+#endif
+
+// Automatically closes |FILE*|s.
+typedef scoped_ptr<FILE, internal::ScopedFILECloser> ScopedFILE;
+
+} // namespace base
+
+#endif // BASE_FILES_SCOPED_FILE_H_
diff --git a/chromium/base/files/scoped_temp_dir_unittest.cc b/chromium/base/files/scoped_temp_dir_unittest.cc
index fe243ce2ee5..da222304a09 100644
--- a/chromium/base/files/scoped_temp_dir_unittest.cc
+++ b/chromium/base/files/scoped_temp_dir_unittest.cc
@@ -5,8 +5,8 @@
#include <string>
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
-#include "base/platform_file.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -98,17 +98,13 @@ TEST(ScopedTempDir, MultipleInvocations) {
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);
+ base::File file(dir.path().Append(FILE_PATH_LITERAL("temp")),
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_EQ(base::File::FILE_OK, file.error_details());
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));
+ file.Close();
// Now, we should be able to delete.
EXPECT_TRUE(dir.Delete());
}
diff --git a/chromium/base/format_macros.h b/chromium/base/format_macros.h
index 466d79be731..4d90c593a61 100644
--- a/chromium/base/format_macros.h
+++ b/chromium/base/format_macros.h
@@ -46,6 +46,34 @@
#define PRIuS "zu"
#endif
+// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit
+// architectures and Apple does not provides standard format macros and
+// recommends casting. This has many drawbacks, so instead define macros
+// for formatting those types.
+#if defined(OS_MACOSX)
+#if defined(ARCH_CPU_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "ld"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "lu"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "lx"
+#endif
+#else // defined(ARCH_CPU_64_BITS)
+#if !defined(PRIdNS)
+#define PRIdNS "d"
+#endif
+#if !defined(PRIuNS)
+#define PRIuNS "u"
+#endif
+#if !defined(PRIxNS)
+#define PRIxNS "x"
+#endif
+#endif
+#endif // defined(OS_MACOSX)
+
#else // OS_WIN
#if !defined(PRId64)
diff --git a/chromium/base/hash.cc b/chromium/base/hash.cc
index 2c87065045b..a7db64a919c 100644
--- a/chromium/base/hash.cc
+++ b/chromium/base/hash.cc
@@ -1,73 +1,18 @@
-// From http://www.azillionmonkeys.com/qed/hash.html
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
#include "base/hash.h"
-typedef uint32 uint32_t;
-typedef uint16 uint16_t;
+// Definition in base/third_party/superfasthash/superfasthash.c. (Third-party
+// code did not come with its own header file, so declaring the function here.)
+// Note: This algorithm is also in Blink under Source/wtf/StringHasher.h.
+extern "C" uint32_t SuperFastHash(const char* data, int len);
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;
+uint32 SuperFastHash(const char* data, int len) {
+ return ::SuperFastHash(data, len);
}
} // namespace base
diff --git a/chromium/base/hash.h b/chromium/base/hash.h
index cf8ea3a26e4..e46f6ac2484 100644
--- a/chromium/base/hash.h
+++ b/chromium/base/hash.h
@@ -5,25 +5,32 @@
#ifndef BASE_HASH_H_
#define BASE_HASH_H_
+#include <limits>
#include <string>
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/logging.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);
+// WARNING: This hash function should not be used for any cryptographic purpose.
+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));
+// Computes a hash of a memory buffer |data| of a given |length|.
+// WARNING: This hash function should not be used for any cryptographic purpose.
+inline uint32 Hash(const char* data, size_t length) {
+ if (length > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ NOTREACHED();
+ return 0;
+ }
+ return SuperFastHash(data, 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()));
+// Computes a hash of a string |str|.
+// WARNING: This hash function should not be used for any cryptographic purpose.
+inline uint32 Hash(const std::string& str) {
+ return Hash(str.data(), str.size());
}
} // namespace base
diff --git a/chromium/base/hash_unittest.cc b/chromium/base/hash_unittest.cc
new file mode 100644
index 00000000000..fc8a7519ee9
--- /dev/null
+++ b/chromium/base/hash_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/hash.h"
+
+#include <string>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(HashTest, String) {
+ std::string str;
+ // Empty string (should hash to 0).
+ str = "";
+ EXPECT_EQ(0u, Hash(str));
+
+ // Simple test.
+ str = "hello world";
+ EXPECT_EQ(2794219650u, Hash(str));
+
+ // Change one bit.
+ str = "helmo world";
+ EXPECT_EQ(1006697176u, Hash(str));
+
+ // Insert a null byte.
+ str = "hello world";
+ str[5] = '\0';
+ EXPECT_EQ(2319902537u, Hash(str));
+
+ // Test that the bytes after the null contribute to the hash.
+ str = "hello worle";
+ str[5] = '\0';
+ EXPECT_EQ(553904462u, Hash(str));
+
+ // Extremely long string.
+ // Also tests strings with high bit set, and null byte.
+ std::vector<char> long_string_buffer;
+ for (int i = 0; i < 4096; ++i)
+ long_string_buffer.push_back((i % 256) - 128);
+ str.assign(&long_string_buffer.front(), long_string_buffer.size());
+ EXPECT_EQ(2797962408u, Hash(str));
+
+ // All possible lengths (mod 4). Tests separate code paths. Also test with
+ // final byte high bit set (regression test for http://crbug.com/90659).
+ // Note that the 1 and 3 cases have a weird bug where the final byte is
+ // treated as a signed char. It was decided on the above bug discussion to
+ // enshrine that behaviour as "correct" to avoid invalidating existing hashes.
+
+ // Length mod 4 == 0.
+ str = "hello w\xab";
+ EXPECT_EQ(615571198u, Hash(str));
+ // Length mod 4 == 1.
+ str = "hello wo\xab";
+ EXPECT_EQ(623474296u, Hash(str));
+ // Length mod 4 == 2.
+ str = "hello wor\xab";
+ EXPECT_EQ(4278562408u, Hash(str));
+ // Length mod 4 == 3.
+ str = "hello worl\xab";
+ EXPECT_EQ(3224633008u, Hash(str));
+}
+
+TEST(HashTest, CString) {
+ const char* str;
+ // Empty string (should hash to 0).
+ str = "";
+ EXPECT_EQ(0u, Hash(str, strlen(str)));
+
+ // Simple test.
+ str = "hello world";
+ EXPECT_EQ(2794219650u, Hash(str, strlen(str)));
+
+ // Ensure that it stops reading after the given length, and does not expect a
+ // null byte.
+ str = "hello world; don't read this part";
+ EXPECT_EQ(2794219650u, Hash(str, strlen("hello world")));
+}
+
+} // namespace base
diff --git a/chromium/base/i18n/break_iterator.cc b/chromium/base/i18n/break_iterator.cc
index 2c4d4669732..fe26a03a0b8 100644
--- a/chromium/base/i18n/break_iterator.cc
+++ b/chromium/base/i18n/break_iterator.cc
@@ -22,6 +22,15 @@ BreakIterator::BreakIterator(const string16& str, BreakType break_type)
pos_(0) {
}
+BreakIterator::BreakIterator(const string16& str, const string16& rules)
+ : iter_(NULL),
+ string_(str),
+ rules_(rules),
+ break_type_(RULE_BASED),
+ prev_(npos),
+ pos_(0) {
+}
+
BreakIterator::~BreakIterator() {
if (iter_)
ubrk_close(static_cast<UBreakIterator*>(iter_));
@@ -29,6 +38,7 @@ BreakIterator::~BreakIterator() {
bool BreakIterator::Init() {
UErrorCode status = U_ZERO_ERROR;
+ UParseError parse_error;
UBreakIteratorType break_type;
switch (break_type_) {
case BREAK_CHARACTER:
@@ -39,19 +49,39 @@ bool BreakIterator::Init() {
break;
case BREAK_LINE:
case BREAK_NEWLINE:
+ case RULE_BASED: // (Keep compiler happy, break_type not used in this case)
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 (break_type_ == RULE_BASED) {
+ iter_ = ubrk_openRules(rules_.c_str(),
+ static_cast<int32_t>(rules_.length()),
+ string_.data(),
+ static_cast<int32_t>(string_.size()),
+ &parse_error,
+ &status);
+ if (U_FAILURE(status)) {
+ NOTREACHED() << "ubrk_openRules failed to parse rule string at line "
+ << parse_error.line << ", offset " << parse_error.offset;
+ }
+ } else {
+ iter_ = ubrk_open(break_type,
+ NULL,
+ string_.data(),
+ static_cast<int32_t>(string_.size()),
+ &status);
+ if (U_FAILURE(status)) {
+ NOTREACHED() << "ubrk_open failed";
+ }
+ }
+
if (U_FAILURE(status)) {
- NOTREACHED() << "ubrk_open failed";
return false;
}
+
// Move the iterator to the beginning of the string.
ubrk_first(static_cast<UBreakIterator*>(iter_));
return true;
@@ -65,6 +95,7 @@ bool BreakIterator::Advance() {
case BREAK_CHARACTER:
case BREAK_WORD:
case BREAK_LINE:
+ case RULE_BASED:
pos = ubrk_next(static_cast<UBreakIterator*>(iter_));
if (pos == UBRK_DONE) {
pos_ = npos;
@@ -91,14 +122,29 @@ bool BreakIterator::Advance() {
}
}
+bool BreakIterator::SetText(const base::char16* text, const size_t length) {
+ UErrorCode status = U_ZERO_ERROR;
+ ubrk_setText(static_cast<UBreakIterator*>(iter_),
+ text, length, &status);
+ pos_ = 0; // implicit when ubrk_setText is done
+ prev_ = npos;
+ if (U_FAILURE(status)) {
+ NOTREACHED() << "ubrk_setText failed";
+ return false;
+ }
+ return true;
+}
+
bool BreakIterator::IsWord() const {
int32_t status = ubrk_getRuleStatus(static_cast<UBreakIterator*>(iter_));
- return (break_type_ == BREAK_WORD && status != UBRK_WORD_NONE);
+ if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+ return false;
+ return status != UBRK_WORD_NONE;
}
bool BreakIterator::IsEndOfWord(size_t position) const {
- if (break_type_ != BREAK_WORD)
- return false;
+ if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+ return false;
UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
@@ -107,8 +153,8 @@ bool BreakIterator::IsEndOfWord(size_t position) const {
}
bool BreakIterator::IsStartOfWord(size_t position) const {
- if (break_type_ != BREAK_WORD)
- return false;
+ if (break_type_ != BREAK_WORD && break_type_ != RULE_BASED)
+ return false;
UBreakIterator* iter = static_cast<UBreakIterator*>(iter_);
UBool boundary = ubrk_isBoundary(iter, static_cast<int32_t>(position));
diff --git a/chromium/base/i18n/break_iterator.h b/chromium/base/i18n/break_iterator.h
index 618a320924e..b34c6770d10 100644
--- a/chromium/base/i18n/break_iterator.h
+++ b/chromium/base/i18n/break_iterator.h
@@ -66,10 +66,17 @@ class BASE_I18N_EXPORT BreakIterator {
BREAK_SPACE = BREAK_LINE,
BREAK_NEWLINE,
BREAK_CHARACTER,
+ // But don't remove this one!
+ RULE_BASED,
};
// Requires |str| to live as long as the BreakIterator does.
BreakIterator(const string16& str, BreakType break_type);
+ // Make a rule-based iterator. BreakType == RULE_BASED is implied.
+ // TODO(andrewhayden): This signature could easily be misinterpreted as
+ // "(const string16& str, const string16& locale)". We should do something
+ // better.
+ BreakIterator(const string16& str, const string16& rules);
~BreakIterator();
// Init() must be called before any of the iterators are valid.
@@ -82,6 +89,11 @@ class BASE_I18N_EXPORT BreakIterator {
// last time Advance() returns true.)
bool Advance();
+ // Updates the text used by the iterator, resetting the iterator as if
+ // if Init() had been called again. Any old state is lost. Returns true
+ // unless there is an error setting the text.
+ bool SetText(const base::char16* text, const size_t length);
+
// 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,
@@ -113,10 +125,13 @@ class BASE_I18N_EXPORT BreakIterator {
// callers from needing access to the ICU public headers directory.
void* iter_;
- // The string we're iterating over.
+ // The string we're iterating over. Can be changed with SetText(...)
const string16& string_;
- // The breaking style (word/space/newline).
+ // Rules for our iterator. Mutually exclusive with break_type_.
+ const string16 rules_;
+
+ // The breaking style (word/space/newline). Mutually exclusive with rules_
BreakType break_type_;
// Previous and current iterator positions.
diff --git a/chromium/base/i18n/build_utf8_validator_tables.cc b/chromium/base/i18n/build_utf8_validator_tables.cc
new file mode 100644
index 00000000000..d37a75172a7
--- /dev/null
+++ b/chromium/base/i18n/build_utf8_validator_tables.cc
@@ -0,0 +1,466 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Create a state machine for validating UTF-8. The algorithm in brief:
+// 1. Convert the complete unicode range of code points, except for the
+// surrogate code points, to an ordered array of sequences of bytes in
+// UTF-8.
+// 2. Convert individual bytes to ranges, starting from the right of each byte
+// sequence. For each range, ensure the bytes on the left and the ranges
+// on the right are the identical.
+// 3. Convert the resulting list of ranges into a state machine, collapsing
+// identical states.
+// 4. Convert the state machine to an array of bytes.
+// 5. Output as a C++ file.
+//
+// To use:
+// $ ninja -C out/Release build_utf8_validator_tables
+// $ out/Release/build_utf8_validator_tables
+// --output=base/i18n/utf8_validator_tables.cc
+// $ git add base/i18n/utf8_validator_tables.cc
+//
+// Because the table is not expected to ever change, it is checked into the
+// repository rather than being regenerated at build time.
+//
+// This code uses type uint8 throughout to represent bytes, to avoid
+// signed/unsigned char confusion.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "third_party/icu/source/common/unicode/utf8.h"
+
+namespace {
+
+const char kHelpText[] =
+ "Usage: build_utf8_validator_tables [ --help ] [ --output=<file> ]\n";
+
+const char kProlog[] =
+ "// Copyright 2013 The Chromium Authors. All rights reserved.\n"
+ "// Use of this source code is governed by a BSD-style license that can "
+ "be\n"
+ "// found in the LICENSE file.\n"
+ "\n"
+ "// This file is auto-generated by build_utf8_validator_tables.\n"
+ "// DO NOT EDIT.\n"
+ "\n"
+ "#include \"base/i18n/utf8_validator_tables.h\"\n"
+ "\n"
+ "namespace base {\n"
+ "namespace internal {\n"
+ "\n"
+ "const uint8 kUtf8ValidatorTables[] = {\n";
+
+const char kEpilog[] =
+ "};\n"
+ "\n"
+ "const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);\n"
+ "\n"
+ "} // namespace internal\n"
+ "} // namespace base\n";
+
+// Ranges are inclusive at both ends--they represent [from, to]
+class Range {
+ public:
+ // Ranges always start with just one byte.
+ explicit Range(uint8 value) : from_(value), to_(value) {}
+
+ // Range objects are copyable and assignable to be used in STL
+ // containers. Since they only contain non-pointer POD types, the default copy
+ // constructor, assignment operator and destructor will work.
+
+ // Add a byte to the range. We intentionally only support adding a byte at the
+ // end, since that is the only operation the code needs.
+ void AddByte(uint8 to) {
+ CHECK(to == to_ + 1);
+ to_ = to;
+ }
+
+ uint8 from() const { return from_; }
+ uint8 to() const { return to_; }
+
+ bool operator<(const Range& rhs) const {
+ return (from() < rhs.from() || (from() == rhs.from() && to() < rhs.to()));
+ }
+
+ bool operator==(const Range& rhs) const {
+ return from() == rhs.from() && to() == rhs.to();
+ }
+
+ private:
+ uint8 from_;
+ uint8 to_;
+};
+
+// A vector of Ranges is like a simple regular expression--it corresponds to
+// a set of strings of the same length that have bytes in each position in
+// the appropriate range.
+typedef std::vector<Range> StringSet;
+
+// A UTF-8 "character" is represented by a sequence of bytes.
+typedef std::vector<uint8> Character;
+
+// In the second stage of the algorithm, we want to convert a large list of
+// Characters into a small list of StringSets.
+struct Pair {
+ Character character;
+ StringSet set;
+};
+
+typedef std::vector<Pair> PairVector;
+
+// A class to print a table of numbers in the same style as clang-format.
+class TablePrinter {
+ public:
+ explicit TablePrinter(FILE* stream)
+ : stream_(stream), values_on_this_line_(0), current_offset_(0) {}
+
+ void PrintValue(uint8 value) {
+ if (values_on_this_line_ == 0) {
+ fputs(" ", stream_);
+ } else if (values_on_this_line_ == kMaxValuesPerLine) {
+ fprintf(stream_, " // 0x%02x\n ", current_offset_);
+ values_on_this_line_ = 0;
+ }
+ fprintf(stream_, " 0x%02x,", static_cast<int>(value));
+ ++values_on_this_line_;
+ ++current_offset_;
+ }
+
+ void NewLine() {
+ while (values_on_this_line_ < kMaxValuesPerLine) {
+ fputs(" ", stream_);
+ ++values_on_this_line_;
+ }
+ fprintf(stream_, " // 0x%02x\n", current_offset_);
+ values_on_this_line_ = 0;
+ }
+
+ private:
+ // stdio stream. Not owned.
+ FILE* stream_;
+
+ // Number of values so far printed on this line.
+ int values_on_this_line_;
+
+ // Total values printed so far.
+ int current_offset_;
+
+ static const int kMaxValuesPerLine = 8;
+
+ DISALLOW_COPY_AND_ASSIGN(TablePrinter);
+};
+
+// Start by filling a PairVector with characters. The resulting vector goes from
+// "\x00" to "\xf4\x8f\xbf\xbf".
+PairVector InitializeCharacters() {
+ PairVector vector;
+ for (int i = 0; i <= 0x10FFFF; ++i) {
+ if (i >= 0xD800 && i < 0xE000) {
+ // Surrogate codepoints are not permitted. Non-character code points are
+ // explicitly permitted.
+ continue;
+ }
+ uint8 bytes[4];
+ unsigned int offset = 0;
+ UBool is_error = false;
+ U8_APPEND(bytes, offset, arraysize(bytes), i, is_error);
+ DCHECK(!is_error);
+ DCHECK_GT(offset, 0u);
+ DCHECK_LE(offset, arraysize(bytes));
+ Pair pair = {Character(bytes, bytes + offset), StringSet()};
+ vector.push_back(pair);
+ }
+ return vector;
+}
+
+// Construct a new Pair from |character| and the concatenation of |new_range|
+// and |existing_set|, and append it to |pairs|.
+void ConstructPairAndAppend(const Character& character,
+ const Range& new_range,
+ const StringSet& existing_set,
+ PairVector* pairs) {
+ Pair new_pair = {character, StringSet(1, new_range)};
+ new_pair.set.insert(
+ new_pair.set.end(), existing_set.begin(), existing_set.end());
+ pairs->push_back(new_pair);
+}
+
+// Each pass over the PairVector strips one byte off the right-hand-side of the
+// characters and adds a range to the set on the right. For example, the first
+// pass converts the range from "\xe0\xa0\x80" to "\xe0\xa0\xbf" to ("\xe0\xa0",
+// [\x80-\xbf]), then the second pass converts the range from ("\xe0\xa0",
+// [\x80-\xbf]) to ("\xe0\xbf", [\x80-\xbf]) to ("\xe0",
+// [\xa0-\xbf][\x80-\xbf]).
+void MoveRightMostCharToSet(PairVector* pairs) {
+ PairVector new_pairs;
+ PairVector::const_iterator it = pairs->begin();
+ while (it != pairs->end() && it->character.empty()) {
+ new_pairs.push_back(*it);
+ ++it;
+ }
+ CHECK(it != pairs->end());
+ Character unconverted_bytes(it->character.begin(), it->character.end() - 1);
+ Range new_range(it->character.back());
+ StringSet converted = it->set;
+ ++it;
+ while (it != pairs->end()) {
+ const Pair& current_pair = *it++;
+ if (current_pair.character.size() == unconverted_bytes.size() + 1 &&
+ std::equal(unconverted_bytes.begin(),
+ unconverted_bytes.end(),
+ current_pair.character.begin()) &&
+ converted == current_pair.set) {
+ // The particular set of UTF-8 codepoints we are validating guarantees
+ // that each byte range will be contiguous. This would not necessarily be
+ // true for an arbitrary set of UTF-8 codepoints.
+ DCHECK_EQ(new_range.to() + 1, current_pair.character.back());
+ new_range.AddByte(current_pair.character.back());
+ continue;
+ }
+ ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs);
+ unconverted_bytes = Character(current_pair.character.begin(),
+ current_pair.character.end() - 1);
+ new_range = Range(current_pair.character.back());
+ converted = current_pair.set;
+ }
+ ConstructPairAndAppend(unconverted_bytes, new_range, converted, &new_pairs);
+ new_pairs.swap(*pairs);
+}
+
+void MoveAllCharsToSets(PairVector* pairs) {
+ // Since each pass of the function moves one character, and UTF-8 sequences
+ // are at most 4 characters long, this simply runs the algorithm four times.
+ for (int i = 0; i < 4; ++i) {
+ MoveRightMostCharToSet(pairs);
+ }
+#if DCHECK_IS_ON
+ for (PairVector::const_iterator it = pairs->begin(); it != pairs->end();
+ ++it) {
+ DCHECK(it->character.empty());
+ }
+#endif
+}
+
+// Logs the generated string sets in regular-expression style, ie. [\x00-\x7f],
+// [\xc2-\xdf][\x80-\xbf], etc. This can be a useful sanity-check that the
+// algorithm is working. Use the command-line option
+// --vmodule=build_utf8_validator_tables=1 to see this output.
+void LogStringSets(const PairVector& pairs) {
+ for (PairVector::const_iterator pair_it = pairs.begin();
+ pair_it != pairs.end();
+ ++pair_it) {
+ std::string set_as_string;
+ for (StringSet::const_iterator set_it = pair_it->set.begin();
+ set_it != pair_it->set.end();
+ ++set_it) {
+ set_as_string += base::StringPrintf("[\\x%02x-\\x%02x]",
+ static_cast<int>(set_it->from()),
+ static_cast<int>(set_it->to()));
+ }
+ VLOG(1) << set_as_string;
+ }
+}
+
+// A single state in the state machine is represented by a sorted vector of
+// start bytes and target states. All input bytes in the range between the start
+// byte and the next entry in the vector (or 0xFF) result in a transition to the
+// target state.
+struct StateRange {
+ uint8 from;
+ uint8 target_state;
+};
+
+typedef std::vector<StateRange> State;
+
+// Generates a state where all bytes go to state 1 (invalid). This is also used
+// as an initialiser for other states (since bytes from outside the desired
+// range are invalid).
+State GenerateInvalidState() {
+ const StateRange range = {0, 1};
+ return State(1, range);
+}
+
+// A map from a state (ie. a set of strings which will match from this state) to
+// a number (which is an index into the array of states).
+typedef std::map<StringSet, uint8> StateMap;
+
+// Create a new state corresponding to |set|, add it |states| and |state_map|
+// and return the index it was given in |states|.
+uint8 MakeState(const StringSet& set,
+ std::vector<State>* states,
+ StateMap* state_map) {
+ DCHECK(!set.empty());
+ const Range& range = set.front();
+ const StringSet rest(set.begin() + 1, set.end());
+ const StateMap::const_iterator where = state_map->find(rest);
+ const uint8 target_state = where == state_map->end()
+ ? MakeState(rest, states, state_map)
+ : where->second;
+ DCHECK_LT(0, range.from());
+ DCHECK_LT(range.to(), 0xFF);
+ const StateRange new_state_initializer[] = {
+ {0, 1}, {range.from(), target_state},
+ {static_cast<uint8>(range.to() + 1), 1}};
+ states->push_back(
+ State(new_state_initializer,
+ new_state_initializer + arraysize(new_state_initializer)));
+ const uint8 new_state_number =
+ base::checked_cast<uint8>(states->size() - 1);
+ CHECK(state_map->insert(std::make_pair(set, new_state_number)).second);
+ return new_state_number;
+}
+
+std::vector<State> GenerateStates(const PairVector& pairs) {
+ // States 0 and 1 are the initial/valid state and invalid state, respectively.
+ std::vector<State> states(2, GenerateInvalidState());
+ StateMap state_map;
+ state_map.insert(std::make_pair(StringSet(), 0));
+ for (PairVector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
+ DCHECK(it->character.empty());
+ DCHECK(!it->set.empty());
+ const Range& range = it->set.front();
+ const StringSet rest(it->set.begin() + 1, it->set.end());
+ const StateMap::const_iterator where = state_map.find(rest);
+ const uint8 target_state = where == state_map.end()
+ ? MakeState(rest, &states, &state_map)
+ : where->second;
+ if (states[0].back().from == range.from()) {
+ DCHECK_EQ(1, states[0].back().target_state);
+ states[0].back().target_state = target_state;
+ DCHECK_LT(range.to(), 0xFF);
+ const StateRange new_range = {static_cast<uint8>(range.to() + 1), 1};
+ states[0].push_back(new_range);
+ } else {
+ DCHECK_LT(range.to(), 0xFF);
+ const StateRange new_range_initializer[] = {{range.from(), target_state},
+ {static_cast<uint8>(range.to() + 1), 1}};
+ states[0]
+ .insert(states[0].end(),
+ new_range_initializer,
+ new_range_initializer + arraysize(new_range_initializer));
+ }
+ }
+ return states;
+}
+
+// Output the generated states as a C++ table. Two tricks are used to compact
+// the table: each state in the table starts with a shift value which indicates
+// how many bits we can discard from the right-hand-side of the byte before
+// doing the table lookup. Secondly, only the state-transitions for bytes
+// with the top-bit set are included in the table; bytes without the top-bit set
+// are just ASCII and are handled directly by the code.
+void PrintStates(const std::vector<State>& states, FILE* stream) {
+ // First calculate the start-offset of each state. This allows the state
+ // machine to jump directly to the correct offset, avoiding an extra
+ // indirection. State 0 starts at offset 0.
+ std::vector<uint8> state_offset(1, 0);
+ std::vector<uint8> shifts;
+ uint8 pos = 0;
+
+ for (std::vector<State>::const_iterator state_it = states.begin();
+ state_it != states.end();
+ ++state_it) {
+ // We want to set |shift| to the (0-based) index of the least-significant
+ // set bit in any of the ranges for this state, since this tells us how many
+ // bits we can discard and still determine what range a byte lies in. Sadly
+ // it appears that ffs() is not portable, so we do it clumsily.
+ uint8 shift = 7;
+ for (State::const_iterator range_it = state_it->begin();
+ range_it != state_it->end();
+ ++range_it) {
+ while (shift > 0 && range_it->from % (1 << shift) != 0) {
+ --shift;
+ }
+ }
+ shifts.push_back(shift);
+ pos += 1 + (1 << (7 - shift));
+ state_offset.push_back(pos);
+ }
+
+ DCHECK_EQ(129, state_offset[1]);
+
+ fputs(kProlog, stream);
+ TablePrinter table_printer(stream);
+
+ for (uint8 state_index = 0; state_index < states.size(); ++state_index) {
+ const uint8 shift = shifts[state_index];
+ uint8 next_range = 0;
+ uint8 target_state = 1;
+ fprintf(stream,
+ " // State %d, offset 0x%02x\n",
+ static_cast<int>(state_index),
+ static_cast<int>(state_offset[state_index]));
+ table_printer.PrintValue(shift);
+ for (int i = 0; i < 0x100; i += (1 << shift)) {
+ if (next_range < states[state_index].size() &&
+ states[state_index][next_range].from == i) {
+ target_state = states[state_index][next_range].target_state;
+ ++next_range;
+ }
+ if (i >= 0x80) {
+ table_printer.PrintValue(state_offset[target_state]);
+ }
+ }
+ table_printer.NewLine();
+ }
+
+ fputs(kEpilog, stream);
+}
+
+} // namespace
+
+int main(int argc, char* argv[]) {
+ CommandLine::Init(argc, argv);
+ logging::LoggingSettings settings;
+ settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+ logging::InitLogging(settings);
+ if (CommandLine::ForCurrentProcess()->HasSwitch("help")) {
+ fwrite(kHelpText, 1, arraysize(kHelpText), stdout);
+ exit(EXIT_SUCCESS);
+ }
+ base::FilePath filename =
+ CommandLine::ForCurrentProcess()->GetSwitchValuePath("output");
+
+ FILE* output = stdout;
+ if (!filename.empty()) {
+ output = base::OpenFile(filename, "wb");
+ if (!output)
+ PLOG(FATAL) << "Couldn't open '" << filename.AsUTF8Unsafe()
+ << "' for writing";
+ }
+
+ // Step 1: Enumerate the characters
+ PairVector pairs = InitializeCharacters();
+ // Step 2: Convert to sets.
+ MoveAllCharsToSets(&pairs);
+ if (VLOG_IS_ON(1)) {
+ LogStringSets(pairs);
+ }
+ // Step 3: Generate states.
+ std::vector<State> states = GenerateStates(pairs);
+ // Step 4/5: Print output
+ PrintStates(states, output);
+
+ if (!filename.empty()) {
+ if (!base::CloseFile(output))
+ PLOG(FATAL) << "Couldn't finish writing '" << filename.AsUTF8Unsafe()
+ << "'";
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/chromium/base/i18n/file_util_icu.cc b/chromium/base/i18n/file_util_icu.cc
index 9b0525086d0..e250c29a5f7 100644
--- a/chromium/base/i18n/file_util_icu.cc
+++ b/chromium/base/i18n/file_util_icu.cc
@@ -97,7 +97,7 @@ void ReplaceIllegalCharactersInPath(base::FilePath::StringType* file_name,
DCHECK(!(IllegalCharacters::GetInstance()->contains(replace_char)));
// Remove leading and trailing whitespace.
- TrimWhitespace(*file_name, TRIM_ALL, file_name);
+ base::TrimWhitespace(*file_name, base::TRIM_ALL, file_name);
IllegalCharacters* illegal = IllegalCharacters::GetInstance();
int cursor = 0; // The ICU macros expect an int.
@@ -145,14 +145,16 @@ bool LocaleAwareCompareFilenames(const base::FilePath& a,
#if defined(OS_WIN)
return base::i18n::CompareString16WithCollator(collator.get(),
- WideToUTF16(a.value()), WideToUTF16(b.value())) == UCOL_LESS;
+ base::WideToUTF16(a.value()), base::WideToUTF16(b.value())) == UCOL_LESS;
#elif defined(OS_POSIX)
// On linux, the file system encoding is not defined. We assume
// SysNativeMBToWide takes care of it.
- return base::i18n::CompareString16WithCollator(collator.get(),
- WideToUTF16(base::SysNativeMBToWide(a.value().c_str())),
- WideToUTF16(base::SysNativeMBToWide(b.value().c_str()))) == UCOL_LESS;
+ return base::i18n::CompareString16WithCollator(
+ collator.get(),
+ base::WideToUTF16(base::SysNativeMBToWide(a.value().c_str())),
+ base::WideToUTF16(base::SysNativeMBToWide(b.value().c_str()))
+ ) == UCOL_LESS;
#else
#error Not implemented on your system
#endif
diff --git a/chromium/base/i18n/file_util_icu_unittest.cc b/chromium/base/i18n/file_util_icu_unittest.cc
index e3af9adf946..dd8122632e8 100644
--- a/chromium/base/i18n/file_util_icu_unittest.cc
+++ b/chromium/base/i18n/file_util_icu_unittest.cc
@@ -73,9 +73,9 @@ TEST_F(FileUtilICUTest, ReplaceIllegalCharactersInPathTest) {
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));
+ std::string bad_name(base::WideToUTF8(kIllegalCharacterCases[i].bad_name));
file_util::ReplaceIllegalCharactersInPath(&bad_name, '-');
- EXPECT_EQ(WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name);
+ EXPECT_EQ(base::WideToUTF8(kIllegalCharacterCases[i].good_name), bad_name);
#endif
}
}
diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc
index e5c698475e4..e0bd62cc50b 100644
--- a/chromium/base/i18n/icu_util.cc
+++ b/chromium/base/i18n/icu_util.cc
@@ -4,8 +4,6 @@
#include "base/i18n/icu_util.h"
-#include "build/build_config.h"
-
#if defined(OS_WIN)
#include <windows.h>
#endif
@@ -30,7 +28,10 @@
#define ICU_UTIL_DATA_STATIC 2
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
-#define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat"
+// Use an unversioned file name to simplify a icu version update down the road.
+// No need to change the filename in multiple places (gyp files, windows
+// build pkg configurations, etc). 'l' stands for Little Endian.
+#define ICU_UTIL_DATA_FILE_NAME "icudtl.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)
@@ -41,14 +42,48 @@
namespace base {
namespace i18n {
+namespace {
+
+#if !defined(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.
+bool g_called_once = false;
+bool g_check_called_once = true;
+#endif
+}
+
+
+#if defined(OS_ANDROID)
+bool InitializeICUWithFileDescriptor(int data_fd) {
+#if !defined(NDEBUG)
+ DCHECK(!g_check_called_once || !g_called_once);
+ g_called_once = true;
+#endif
+
+#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
+ // The ICU data is statically linked.
+ return true;
+#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
+ CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ());
+ if (!mapped_file.IsValid()) {
+ if (!mapped_file.Initialize(base::File(data_fd))) {
+ LOG(ERROR) << "Couldn't mmap icu data file";
+ return false;
+ }
+ }
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
+ return err == U_ZERO_ERROR;
+#endif // ICU_UTIL_DATA_FILE
+}
+#endif
+
+
bool InitializeICU() {
-#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;
+#if !defined(NDEBUG)
+ DCHECK(!g_check_called_once || !g_called_once);
+ g_called_once = true;
#endif
#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
@@ -59,13 +94,13 @@ bool InitializeICU() {
HMODULE module = LoadLibrary(data_path.value().c_str());
if (!module) {
- DLOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
+ LOG(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 "
+ LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
<< ICU_UTIL_DATA_SHARED_MODULE_NAME;
return false;
}
@@ -86,25 +121,32 @@ bool InitializeICU() {
// be released.
CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ());
if (!mapped_file.IsValid()) {
- // Assume it is in the framework bundle's Resources directory.
#if !defined(OS_MACOSX)
+ FilePath data_path;
+#if defined(OS_WIN)
+ // The data file will be in the same directory as the current module.
+ bool path_ok = PathService::Get(base::DIR_MODULE, &data_path);
+#elif defined(OS_ANDROID)
+ bool path_ok = PathService::Get(base::DIR_ANDROID_APP_DATA, &data_path);
+#else
// 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);
+#endif
DCHECK(path_ok);
data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME);
#else
+ // 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";
+ LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle";
return false;
}
#endif // OS check
if (!mapped_file.Initialize(data_path)) {
- DLOG(ERROR) << "Couldn't mmap " << data_path.value();
+ LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe();
return false;
}
}
@@ -114,5 +156,11 @@ bool InitializeICU() {
#endif
}
+void AllowMultipleInitializeCallsForTesting() {
+#if !defined(NDEBUG)
+ g_check_called_once = false;
+#endif
+}
+
} // namespace i18n
} // namespace base
diff --git a/chromium/base/i18n/icu_util.h b/chromium/base/i18n/icu_util.h
index ef5dede235d..b0a5dbcce6d 100644
--- a/chromium/base/i18n/icu_util.h
+++ b/chromium/base/i18n/icu_util.h
@@ -5,6 +5,7 @@
#ifndef BASE_I18N_ICU_UTIL_H_
#define BASE_I18N_ICU_UTIL_H_
+#include "build/build_config.h"
#include "base/i18n/base_i18n_export.h"
namespace base {
@@ -14,6 +15,15 @@ namespace i18n {
// function should be called before ICU is used.
BASE_I18N_EXPORT bool InitializeICU();
+#if defined(OS_ANDROID)
+// Android uses a file descriptor passed by browser process to initialize ICU
+// in render processes.
+BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(int data_fd);
+#endif
+
+// In a test binary, the call above might occur twice.
+BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
+
} // namespace i18n
} // namespace base
diff --git a/chromium/base/i18n/rtl.cc b/chromium/base/i18n/rtl.cc
index d9818e800e2..d878c40da02 100644
--- a/chromium/base/i18n/rtl.cc
+++ b/chromium/base/i18n/rtl.cc
@@ -14,10 +14,6 @@
#include "third_party/icu/source/common/unicode/uscript.h"
#include "third_party/icu/source/i18n/unicode/coll.h"
-#if defined(TOOLKIT_GTK)
-#include <gtk/gtk.h>
-#endif
-
namespace {
// Extract language, country and variant, but ignore keywords. For example,
@@ -123,12 +119,7 @@ void SetICUDefaultLocale(const std::string& locale_string) {
}
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() {
@@ -163,6 +154,21 @@ TextDirection GetFirstStrongCharacterDirection(const string16& text) {
return LEFT_TO_RIGHT;
}
+TextDirection GetLastStrongCharacterDirection(const string16& text) {
+ const UChar* string = text.c_str();
+ size_t position = text.length();
+ while (position > 0) {
+ UChar32 character;
+ size_t prev_position = position;
+ U16_PREV(string, 0, prev_position, character);
+ TextDirection direction = GetCharacterDirection(character);
+ if (direction != UNKNOWN_DIRECTION)
+ return direction;
+ position = prev_position;
+ }
+ return LEFT_TO_RIGHT;
+}
+
TextDirection GetStringDirection(const string16& text) {
const UChar* string = text.c_str();
size_t length = text.length();
@@ -248,15 +254,18 @@ bool AdjustStringForLocaleDirection(string16* text) {
bool has_rtl_chars = StringContainsStrongRTLChars(*text);
if (!ui_direction_is_rtl && has_rtl_chars) {
WrapStringWithRTLFormatting(text);
- text->insert(0U, 1U, kLeftToRightMark);
+ text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+ kLeftToRightMark);
text->push_back(kLeftToRightMark);
} else if (ui_direction_is_rtl && has_rtl_chars) {
WrapStringWithRTLFormatting(text);
- text->insert(0U, 1U, kRightToLeftMark);
+ text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+ kRightToLeftMark);
text->push_back(kRightToLeftMark);
} else if (ui_direction_is_rtl) {
WrapStringWithLTRFormatting(text);
- text->insert(0U, 1U, kRightToLeftMark);
+ text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+ kRightToLeftMark);
text->push_back(kRightToLeftMark);
} else {
return false;
@@ -317,7 +326,8 @@ void WrapStringWithLTRFormatting(string16* text) {
return;
// Inserting an LRE (Left-To-Right Embedding) mark as the first character.
- text->insert(0U, 1U, kLeftToRightEmbeddingMark);
+ text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+ kLeftToRightEmbeddingMark);
// Inserting a PDF (Pop Directional Formatting) mark as the last character.
text->push_back(kPopDirectionalFormatting);
@@ -328,7 +338,8 @@ void WrapStringWithRTLFormatting(string16* text) {
return;
// Inserting an RLE (Right-To-Left Embedding) mark as the first character.
- text->insert(0U, 1U, kRightToLeftEmbeddingMark);
+ text->insert(static_cast<size_t>(0), static_cast<size_t>(1),
+ kRightToLeftEmbeddingMark);
// Inserting a PDF (Pop Directional Formatting) mark as the last character.
text->push_back(kPopDirectionalFormatting);
diff --git a/chromium/base/i18n/rtl.h b/chromium/base/i18n/rtl.h
index c80d2f85777..aa5f6810a9b 100644
--- a/chromium/base/i18n/rtl.h
+++ b/chromium/base/i18n/rtl.h
@@ -64,7 +64,7 @@ BASE_I18N_EXPORT bool ICUIsRTL();
BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale(
const char* locale_name);
-// Given the string in |text|, returns the directionality of the first
+// Given the string in |text|, returns the directionality of the first or last
// 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
@@ -72,6 +72,8 @@ BASE_I18N_EXPORT TextDirection GetTextDirectionForLocale(
// for more information.
BASE_I18N_EXPORT TextDirection GetFirstStrongCharacterDirection(
const string16& text);
+BASE_I18N_EXPORT TextDirection GetLastStrongCharacterDirection(
+ const string16& text);
// Given the string in |text|, returns LEFT_TO_RIGHT or RIGHT_TO_LEFT if all the
// strong directionality characters in the string are of the same
diff --git a/chromium/base/i18n/rtl_unittest.cc b/chromium/base/i18n/rtl_unittest.cc
index 58772b05839..2d923acf0ba 100644
--- a/chromium/base/i18n/rtl_unittest.cc
+++ b/chromium/base/i18n/rtl_unittest.cc
@@ -14,10 +14,6 @@
#include "testing/platform_test.h"
#include "third_party/icu/source/i18n/unicode/usearch.h"
-#if defined(TOOLKIT_GTK)
-#include <gtk/gtk.h>
-#endif
-
namespace base {
namespace i18n {
@@ -27,10 +23,6 @@ namespace {
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());
}
@@ -46,6 +38,8 @@ TEST_F(RTLTest, GetFirstStrongCharacterDirection) {
} cases[] = {
// Test pure LTR string.
{ L"foo bar", LEFT_TO_RIGHT },
+ // Test pure RTL string.
+ { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT},
// Test bidi string in which the first character with strong directionality
// is a character with type L.
{ L"foo \x05d0 bar", LEFT_TO_RIGHT },
@@ -107,6 +101,68 @@ TEST_F(RTLTest, GetFirstStrongCharacterDirection) {
GetFirstStrongCharacterDirection(WideToUTF16(cases[i].text)));
}
+
+// Note that the cases with LRE, LRO, RLE and RLO are invalid for
+// GetLastStrongCharacterDirection because they should be followed by PDF
+// character.
+TEST_F(RTLTest, GetLastStrongCharacterDirection) {
+ struct {
+ const wchar_t* text;
+ TextDirection direction;
+ } cases[] = {
+ // Test pure LTR string.
+ { L"foo bar", LEFT_TO_RIGHT },
+ // Test pure RTL string.
+ { L"\x05d0\x05d1\x05d2 \x05d3\x0d4\x05d5", RIGHT_TO_LEFT},
+ // Test bidi string in which the last character with strong directionality
+ // is a character with type L.
+ { L"foo \x05d0 bar", LEFT_TO_RIGHT },
+ // Test bidi string in which the last character with strong directionality
+ // is a character with type R.
+ { L"\x05d0 foo bar \x05d3", RIGHT_TO_LEFT },
+ // Test bidi string which ends with a character with weak directionality
+ // and in which the last character with strong directionality is a
+ // character with type L.
+ { L"!foo \x05d0 bar!", LEFT_TO_RIGHT },
+ // Test bidi string which ends with a character with weak directionality
+ // and in which the last character with strong directionality is a
+ // character with type R.
+ { L",\x05d0 foo bar \x05d1,", RIGHT_TO_LEFT },
+ // Test bidi string in which the last character with strong directionality
+ // is a character with type AL.
+ { L"\x0622 foo \x05d0 bar \x0622", 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"abc 123" L" ! \x10910 !",
+#elif defined(WCHAR_T_IS_UTF16)
+ L"abc 123" L" ! \xd802\xdd10 !",
+#else
+#error wchar_t should be either UTF-16 or UTF-32
+#endif
+ RIGHT_TO_LEFT },
+ {
+#if defined(WCHAR_T_IS_UTF32)
+ L"abc 123" L" ! \x10401 !",
+#elif defined(WCHAR_T_IS_UTF16)
+ L"abc 123" L" ! \xd801\xdc01 !",
+#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,
+ GetLastStrongCharacterDirection(WideToUTF16(cases[i].text)));
+}
+
TEST_F(RTLTest, GetStringDirection) {
struct {
const wchar_t* text;
diff --git a/chromium/base/i18n/streaming_utf8_validator.cc b/chromium/base/i18n/streaming_utf8_validator.cc
new file mode 100644
index 00000000000..c809985e168
--- /dev/null
+++ b/chromium/base/i18n/streaming_utf8_validator.cc
@@ -0,0 +1,59 @@
+// Copyright 2014 The Chromium 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 implementation doesn't use ICU. The ICU macros are oriented towards
+// character-at-a-time processing, whereas byte-at-a-time processing is easier
+// with streaming input.
+
+#include "base/i18n/streaming_utf8_validator.h"
+
+#include "base/i18n/utf8_validator_tables.h"
+#include "base/logging.h"
+
+namespace base {
+namespace {
+
+uint8 StateTableLookup(uint8 offset) {
+ DCHECK_LT(offset, internal::kUtf8ValidatorTablesSize);
+ return internal::kUtf8ValidatorTables[offset];
+}
+
+} // namespace
+
+StreamingUtf8Validator::State StreamingUtf8Validator::AddBytes(const char* data,
+ size_t size) {
+ // Copy |state_| into a local variable so that the compiler doesn't have to be
+ // careful of aliasing.
+ uint8 state = state_;
+ for (const char* p = data; p != data + size; ++p) {
+ if ((*p & 0x80) == 0) {
+ if (state == 0)
+ continue;
+ state = internal::I18N_UTF8_VALIDATOR_INVALID_INDEX;
+ break;
+ }
+ const uint8 shift_amount = StateTableLookup(state);
+ const uint8 shifted_char = (*p & 0x7F) >> shift_amount;
+ state = StateTableLookup(state + shifted_char + 1);
+ // State may be INVALID here, but this code is optimised for the case of
+ // valid UTF-8 and it is more efficient (by about 2%) to not attempt an
+ // early loop exit unless we hit an ASCII character.
+ }
+ state_ = state;
+ return state == 0 ? VALID_ENDPOINT
+ : state == internal::I18N_UTF8_VALIDATOR_INVALID_INDEX
+ ? INVALID
+ : VALID_MIDPOINT;
+}
+
+void StreamingUtf8Validator::Reset() {
+ state_ = 0u;
+}
+
+bool StreamingUtf8Validator::Validate(const std::string& string) {
+ return StreamingUtf8Validator().AddBytes(string.data(), string.size()) ==
+ VALID_ENDPOINT;
+}
+
+} // namespace base
diff --git a/chromium/base/i18n/streaming_utf8_validator.h b/chromium/base/i18n/streaming_utf8_validator.h
new file mode 100644
index 00000000000..f10ac721c54
--- /dev/null
+++ b/chromium/base/i18n/streaming_utf8_validator.h
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium 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 streaming validator for UTF-8. Validation is based on the definition in
+// RFC-3629. In particular, it does not reject the invalid characters rejected
+// by base::IsStringUTF8().
+//
+// The implementation detects errors on the first possible byte.
+
+#ifndef BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
+#define BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+class BASE_I18N_EXPORT StreamingUtf8Validator {
+ public:
+ // The validator exposes 3 states. It starts in state VALID_ENDPOINT. As it
+ // processes characters it alternates between VALID_ENDPOINT and
+ // VALID_MIDPOINT. If it encounters an invalid byte or UTF-8 sequence the
+ // state changes permanently to INVALID.
+ enum State {
+ VALID_ENDPOINT,
+ VALID_MIDPOINT,
+ INVALID
+ };
+
+ StreamingUtf8Validator() : state_(0u) {}
+ // Trivial destructor intentionally omitted.
+
+ // Validate |size| bytes starting at |data|. If the concatenation of all calls
+ // to AddBytes() since this object was constructed or reset is a valid UTF-8
+ // string, returns VALID_ENDPOINT. If it could be the prefix of a valid UTF-8
+ // string, returns VALID_MIDPOINT. If an invalid byte or UTF-8 sequence was
+ // present, returns INVALID.
+ State AddBytes(const char* data, size_t size);
+
+ // Return the object to a freshly-constructed state so that it can be re-used.
+ void Reset();
+
+ // Validate a complete string using the same criteria. Returns true if the
+ // string only contains complete, valid UTF-8 codepoints.
+ static bool Validate(const std::string& string);
+
+ private:
+ // The current state of the validator. Value 0 is the initial/valid state.
+ // The state is stored as an offset into |kUtf8ValidatorTables|. The special
+ // state |kUtf8InvalidState| is invalid.
+ uint8 state_;
+
+ // This type could be made copyable but there is currently no use-case for
+ // it.
+ DISALLOW_COPY_AND_ASSIGN(StreamingUtf8Validator);
+};
+
+} // namespace base
+
+#endif // BASE_I18N_STREAMING_UTF8_VALIDATOR_H_
diff --git a/chromium/base/i18n/streaming_utf8_validator_perftest.cc b/chromium/base/i18n/streaming_utf8_validator_perftest.cc
new file mode 100644
index 00000000000..ac2eb0820ba
--- /dev/null
+++ b/chromium/base/i18n/streaming_utf8_validator_perftest.cc
@@ -0,0 +1,234 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// All data that is passed through a WebSocket with type "Text" needs to be
+// validated as UTF8. Since this is done on the IO thread, it needs to be
+// reasonably fast.
+
+// We are only interested in the performance on valid UTF8. Invalid UTF8 will
+// result in a connection failure, so is unlikely to become a source of
+// performance issues.
+
+#include "base/i18n/streaming_utf8_validator.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/perf_time_logger.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+// We want to test ranges of valid UTF-8 sequences. These ranges are inclusive.
+// They are intended to be large enough that the validator needs to do
+// meaningful work while being in some sense "realistic" (eg. control characters
+// are not included).
+const char kOneByteSeqRangeStart[] = " "; // U+0020
+const char kOneByteSeqRangeEnd[] = "~"; // U+007E
+
+const char kTwoByteSeqRangeStart[] = "\xc2\xa0"; // U+00A0 non-breaking space
+const char kTwoByteSeqRangeEnd[] = "\xc9\x8f"; // U+024F small y with stroke
+
+const char kThreeByteSeqRangeStart[] = "\xe3\x81\x82"; // U+3042 Hiragana "a"
+const char kThreeByteSeqRangeEnd[] = "\xe9\xbf\x83"; // U+9FC3 "to blink"
+
+const char kFourByteSeqRangeStart[] = "\xf0\xa0\x80\x8b"; // U+2000B
+const char kFourByteSeqRangeEnd[] = "\xf0\xaa\x9a\xb2"; // U+2A6B2
+
+// The different lengths of strings to test.
+const size_t kTestLengths[] = {1, 32, 256, 32768, 1 << 20};
+
+// Simplest possible byte-at-a-time validator, to provide a baseline
+// for comparison. This is only tried on 1-byte UTF-8 sequences, as
+// the results will not be meaningful with sequences containing
+// top-bit-set bytes.
+bool IsString7Bit(const std::string& s) {
+ for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {
+ if (*it & 0x80)
+ return false;
+ }
+ return true;
+}
+
+// Assumes that |previous| is a valid UTF-8 sequence, and attempts to return
+// the next one. Is just barely smart enough to iterate through the ranges
+// defined about.
+std::string NextUtf8Sequence(const std::string& previous) {
+ DCHECK(StreamingUtf8Validator::Validate(previous));
+ std::string next = previous;
+ for (int i = static_cast<int>(previous.length() - 1); i >= 0; --i) {
+ // All bytes in a UTF-8 sequence except the first one are
+ // constrained to the range 0x80 to 0xbf, inclusive. When we
+ // increment past 0xbf, we carry into the previous byte.
+ if (i > 0 && next[i] == '\xbf') {
+ next[i] = '\x80';
+ continue; // carry
+ }
+ ++next[i];
+ break; // no carry
+ }
+ DCHECK(StreamingUtf8Validator::Validate(next))
+ << "Result \"" << next << "\" failed validation";
+ return next;
+}
+
+typedef bool (*TestTargetType)(const std::string&);
+
+// Run fuction |target| over |test_string| |times| times, and report the results
+// using |description|.
+bool RunTest(const std::string& description,
+ TestTargetType target,
+ const std::string& test_string,
+ int times) {
+ base::PerfTimeLogger timer(description.c_str());
+ bool result = true;
+ for (int i = 0; i < times; ++i) {
+ result = target(test_string) && result;
+ }
+ timer.Done();
+ return result;
+}
+
+// Construct a string by repeating |input| enough times to equal or exceed
+// |length|.
+std::string ConstructRepeatedTestString(const std::string& input,
+ size_t length) {
+ std::string output = input;
+ while (output.length() * 2 < length) {
+ output += output;
+ }
+ if (output.length() < length) {
+ output += ConstructRepeatedTestString(input, length - output.length());
+ }
+ return output;
+}
+
+// Construct a string by expanding the range of UTF-8 sequences
+// between |input_start| and |input_end|, inclusive, and then
+// repeating the resulting string until it equals or exceeds |length|
+// bytes. |input_start| and |input_end| must be valid UTF-8
+// sequences.
+std::string ConstructRangedTestString(const std::string& input_start,
+ const std::string& input_end,
+ size_t length) {
+ std::string output = input_start;
+ std::string input = input_start;
+ while (output.length() < length && input != input_end) {
+ input = NextUtf8Sequence(input);
+ output += input;
+ }
+ if (output.length() < length) {
+ output = ConstructRepeatedTestString(output, length);
+ }
+ return output;
+}
+
+struct TestFunctionDescription {
+ TestTargetType function;
+ const char* function_name;
+};
+
+// IsString7Bit is intentionally placed last so it can be excluded easily.
+const TestFunctionDescription kTestFunctions[] = {
+ {&StreamingUtf8Validator::Validate, "StreamingUtf8Validator"},
+ {&IsStringUTF8, "IsStringUTF8"}, {&IsString7Bit, "IsString7Bit"}};
+
+// Construct a test string from |construct_test_string| for each of the lengths
+// in |kTestLengths| in turn. For each string, run each test in |test_functions|
+// for a number of iterations such that the total number of bytes validated
+// is around 16MB.
+void RunSomeTests(
+ const char format[],
+ base::Callback<std::string(size_t length)> construct_test_string,
+ const TestFunctionDescription* test_functions,
+ size_t test_count) {
+ for (size_t i = 0; i < arraysize(kTestLengths); ++i) {
+ const size_t length = kTestLengths[i];
+ const std::string test_string = construct_test_string.Run(length);
+ const int real_length = static_cast<int>(test_string.length());
+ const int times = (1 << 24) / real_length;
+ for (size_t test_index = 0; test_index < test_count; ++test_index) {
+ EXPECT_TRUE(RunTest(StringPrintf(format,
+ test_functions[test_index].function_name,
+ real_length,
+ times),
+ test_functions[test_index].function,
+ test_string,
+ times));
+ }
+ }
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, OneByteRepeated) {
+ RunSomeTests("%s: bytes=1 repeated length=%d repeat=%d",
+ base::Bind(ConstructRepeatedTestString, kOneByteSeqRangeStart),
+ kTestFunctions,
+ 3);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, OneByteRange) {
+ RunSomeTests("%s: bytes=1 ranged length=%d repeat=%d",
+ base::Bind(ConstructRangedTestString,
+ kOneByteSeqRangeStart,
+ kOneByteSeqRangeEnd),
+ kTestFunctions,
+ 3);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, TwoByteRepeated) {
+ RunSomeTests("%s: bytes=2 repeated length=%d repeat=%d",
+ base::Bind(ConstructRepeatedTestString, kTwoByteSeqRangeStart),
+ kTestFunctions,
+ 2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, TwoByteRange) {
+ RunSomeTests("%s: bytes=2 ranged length=%d repeat=%d",
+ base::Bind(ConstructRangedTestString,
+ kTwoByteSeqRangeStart,
+ kTwoByteSeqRangeEnd),
+ kTestFunctions,
+ 2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRepeated) {
+ RunSomeTests(
+ "%s: bytes=3 repeated length=%d repeat=%d",
+ base::Bind(ConstructRepeatedTestString, kThreeByteSeqRangeStart),
+ kTestFunctions,
+ 2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, ThreeByteRange) {
+ RunSomeTests("%s: bytes=3 ranged length=%d repeat=%d",
+ base::Bind(ConstructRangedTestString,
+ kThreeByteSeqRangeStart,
+ kThreeByteSeqRangeEnd),
+ kTestFunctions,
+ 2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, FourByteRepeated) {
+ RunSomeTests("%s: bytes=4 repeated length=%d repeat=%d",
+ base::Bind(ConstructRepeatedTestString, kFourByteSeqRangeStart),
+ kTestFunctions,
+ 2);
+}
+
+TEST(StreamingUtf8ValidatorPerfTest, FourByteRange) {
+ RunSomeTests("%s: bytes=4 ranged length=%d repeat=%d",
+ base::Bind(ConstructRangedTestString,
+ kFourByteSeqRangeStart,
+ kFourByteSeqRangeEnd),
+ kTestFunctions,
+ 2);
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/i18n/streaming_utf8_validator_unittest.cc b/chromium/base/i18n/streaming_utf8_validator_unittest.cc
new file mode 100644
index 00000000000..20ea564c032
--- /dev/null
+++ b/chromium/base/i18n/streaming_utf8_validator_unittest.cc
@@ -0,0 +1,412 @@
+// Copyright 2014 The Chromium Authors. All 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/streaming_utf8_validator.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+#include "base/strings/string_piece.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST to verify that this class
+// accepts exactly the same set of 4-byte strings as ICU-based validation. This
+// tests every possible 4-byte string, so it is too slow to run routinely on
+// low-powered machines.
+//
+// #define BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "third_party/icu/source/common/unicode/utf8.h"
+
+#endif // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+namespace base {
+namespace {
+
+// Avoid having to qualify the enum values in the tests.
+const StreamingUtf8Validator::State VALID_ENDPOINT =
+ StreamingUtf8Validator::VALID_ENDPOINT;
+const StreamingUtf8Validator::State VALID_MIDPOINT =
+ StreamingUtf8Validator::VALID_MIDPOINT;
+const StreamingUtf8Validator::State INVALID = StreamingUtf8Validator::INVALID;
+
+#ifdef BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+const uint32 kThoroughTestChunkSize = 1 << 24;
+
+class StreamingUtf8ValidatorThoroughTest : public ::testing::Test {
+ protected:
+ StreamingUtf8ValidatorThoroughTest()
+ : all_done_(&lock_), tasks_dispatched_(0), tasks_finished_(0) {}
+
+ // This uses the same logic as base::IsStringUTF8 except it considers
+ // non-characters valid (and doesn't require a string as input).
+ static bool IsStringUtf8(const char* src, int32 src_len) {
+ int32 char_index = 0;
+
+ while (char_index < src_len) {
+ int32 code_point;
+ U8_NEXT(src, char_index, src_len, code_point);
+ if (!base::IsValidCodepoint(code_point))
+ return false;
+ }
+ return true;
+ }
+
+ // Converts the passed-in integer to a 4 byte string and then
+ // verifies that IsStringUtf8 and StreamingUtf8Validator agree on
+ // whether it is valid UTF-8 or not.
+ void TestNumber(uint32 n) const {
+ char test[sizeof n];
+ memcpy(test, &n, sizeof n);
+ StreamingUtf8Validator validator;
+ EXPECT_EQ(IsStringUtf8(test, sizeof n),
+ validator.AddBytes(test, sizeof n) == VALID_ENDPOINT)
+ << "Difference of opinion for \""
+ << base::StringPrintf("\\x%02X\\x%02X\\x%02X\\x%02X",
+ test[0] & 0xFF,
+ test[1] & 0xFF,
+ test[2] & 0xFF,
+ test[3] & 0xFF) << "\"";
+ }
+
+ public:
+ // Tests the 4-byte sequences corresponding to the |size| integers
+ // starting at |begin|. This is intended to be run from a worker
+ // pool. Signals |all_done_| at the end if it thinks all tasks are
+ // finished.
+ void TestRange(uint32 begin, uint32 size) {
+ for (uint32 i = 0; i < size; ++i) {
+ TestNumber(begin + i);
+ }
+ base::AutoLock al(lock_);
+ ++tasks_finished_;
+ LOG(INFO) << tasks_finished_ << " / " << tasks_dispatched_
+ << " tasks done\n";
+ if (tasks_finished_ >= tasks_dispatched_) {
+ all_done_.Signal();
+ }
+ }
+
+ protected:
+ base::Lock lock_;
+ base::ConditionVariable all_done_;
+ int tasks_dispatched_;
+ int tasks_finished_;
+};
+
+TEST_F(StreamingUtf8ValidatorThoroughTest, TestEverything) {
+ scoped_refptr<base::SequencedWorkerPool> pool =
+ new base::SequencedWorkerPool(32, "TestEverything");
+ base::AutoLock al(lock_);
+ uint32 begin = 0;
+ do {
+ pool->PostWorkerTask(
+ FROM_HERE,
+ base::Bind(&StreamingUtf8ValidatorThoroughTest::TestRange,
+ base::Unretained(this),
+ begin,
+ kThoroughTestChunkSize));
+ ++tasks_dispatched_;
+ begin += kThoroughTestChunkSize;
+ } while (begin != 0);
+ while (tasks_finished_ < tasks_dispatched_)
+ all_done_.Wait();
+}
+
+#endif // BASE_I18N_UTF8_VALIDATOR_THOROUGH_TEST
+
+// These valid and invalid UTF-8 sequences are based on the tests from
+// base/strings/string_util_unittest.cc
+
+// All of the strings in |valid| must represent a single codepoint, because
+// partial sequences are constructed by taking non-empty prefixes of these
+// strings.
+const char* const valid[] = {"\r", "\n", "a",
+ "\xc2\x81", "\xe1\x80\xbf", "\xf1\x80\xa0\xbf",
+ "\xef\xbb\xbf", // UTF-8 BOM
+};
+
+const char* const* const valid_end = valid + arraysize(valid);
+
+const char* const invalid[] = {
+ // always invalid bytes
+ "\xc0", "\xc1",
+ "\xf5", "\xf6", "\xf7",
+ "\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff",
+ // surrogate code points
+ "\xed\xa0\x80", "\xed\x0a\x8f", "\xed\xbf\xbf",
+ //
+ // overlong sequences
+ "\xc0\x80" // U+0000
+ "\xc1\x80", // "A"
+ "\xc1\x81", // "B"
+ "\xe0\x80\x80", // U+0000
+ "\xe0\x82\x80", // U+0080
+ "\xe0\x9f\xbf", // U+07ff
+ "\xf0\x80\x80\x8D", // U+000D
+ "\xf0\x80\x82\x91", // U+0091
+ "\xf0\x80\xa0\x80", // U+0800
+ "\xf0\x8f\xbb\xbf", // U+FEFF (BOM)
+ "\xf8\x80\x80\x80\xbf", // U+003F
+ "\xfc\x80\x80\x80\xa0\xa5",
+ //
+ // Beyond U+10FFFF
+ "\xf4\x90\x80\x80", // U+110000
+ "\xf8\xa0\xbf\x80\xbf", // 5 bytes
+ "\xfc\x9c\xbf\x80\xbf\x80", // 6 bytes
+ //
+ // BOMs in UTF-16(BE|LE)
+ "\xfe\xff", "\xff\xfe",
+};
+
+const char* const* const invalid_end = invalid + arraysize(invalid);
+
+// A ForwardIterator which returns all the non-empty prefixes of the elements of
+// "valid".
+class PartialIterator {
+ public:
+ // The constructor returns the first iterator, ie. it is equivalent to
+ // begin().
+ PartialIterator() : index_(0), prefix_length_(0) { Advance(); }
+ // The trivial destructor left intentionally undefined.
+ // This is a value type; the default copy constructor and assignment operator
+ // generated by the compiler are used.
+
+ static PartialIterator end() { return PartialIterator(arraysize(valid), 1); }
+
+ PartialIterator& operator++() {
+ Advance();
+ return *this;
+ }
+
+ base::StringPiece operator*() const {
+ return base::StringPiece(valid[index_], prefix_length_);
+ }
+
+ bool operator==(const PartialIterator& rhs) const {
+ return index_ == rhs.index_ && prefix_length_ == rhs.prefix_length_;
+ }
+
+ bool operator!=(const PartialIterator& rhs) const { return !(rhs == *this); }
+
+ private:
+ // This constructor is used by the end() method.
+ PartialIterator(size_t index, size_t prefix_length)
+ : index_(index), prefix_length_(prefix_length) {}
+
+ void Advance() {
+ if (index_ < arraysize(valid) && prefix_length_ < strlen(valid[index_]))
+ ++prefix_length_;
+ while (index_ < arraysize(valid) &&
+ prefix_length_ == strlen(valid[index_])) {
+ ++index_;
+ prefix_length_ = 1;
+ }
+ }
+
+ // The UTF-8 sequence, as an offset into the |valid| array.
+ size_t index_;
+ size_t prefix_length_;
+};
+
+// A test fixture for tests which test one UTF-8 sequence (or invalid
+// byte sequence) at a time.
+class StreamingUtf8ValidatorSingleSequenceTest : public ::testing::Test {
+ protected:
+ // Iterator must be convertible when de-referenced to StringPiece.
+ template <typename Iterator>
+ void CheckRange(Iterator begin,
+ Iterator end,
+ StreamingUtf8Validator::State expected) {
+ for (Iterator it = begin; it != end; ++it) {
+ StreamingUtf8Validator validator;
+ base::StringPiece sequence = *it;
+ EXPECT_EQ(expected,
+ validator.AddBytes(sequence.data(), sequence.size()))
+ << "Failed for \"" << sequence << "\"";
+ }
+ }
+
+ // Adding input a byte at a time should make absolutely no difference.
+ template <typename Iterator>
+ void CheckRangeByteAtATime(Iterator begin,
+ Iterator end,
+ StreamingUtf8Validator::State expected) {
+ for (Iterator it = begin; it != end; ++it) {
+ StreamingUtf8Validator validator;
+ base::StringPiece sequence = *it;
+ StreamingUtf8Validator::State state = VALID_ENDPOINT;
+ for (base::StringPiece::const_iterator cit = sequence.begin();
+ cit != sequence.end();
+ ++cit) {
+ state = validator.AddBytes(&*cit, 1);
+ }
+ EXPECT_EQ(expected, state) << "Failed for \"" << sequence << "\"";
+ }
+ }
+};
+
+// A test fixture for tests which test the concatenation of byte sequences.
+class StreamingUtf8ValidatorDoubleSequenceTest : public ::testing::Test {
+ protected:
+ // Check every possible concatenation of byte sequences from two
+ // ranges, and verify that the combination matches the expected
+ // state.
+ template <typename Iterator1, typename Iterator2>
+ void CheckCombinations(Iterator1 begin1,
+ Iterator1 end1,
+ Iterator2 begin2,
+ Iterator2 end2,
+ StreamingUtf8Validator::State expected) {
+ StreamingUtf8Validator validator;
+ for (Iterator1 it1 = begin1; it1 != end1; ++it1) {
+ base::StringPiece c1 = *it1;
+ for (Iterator2 it2 = begin2; it2 != end2; ++it2) {
+ base::StringPiece c2 = *it2;
+ validator.AddBytes(c1.data(), c1.size());
+ EXPECT_EQ(expected, validator.AddBytes(c2.data(), c2.size()))
+ << "Failed for \"" << c1 << c2 << "\"";
+ validator.Reset();
+ }
+ }
+ }
+};
+
+TEST(StreamingUtf8ValidatorTest, NothingIsValid) {
+ static const char kNothing[] = "";
+ EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNothing, 0));
+}
+
+// Because the members of the |valid| array need to be non-zero length
+// sequences and are measured with strlen(), |valid| cannot be used it
+// to test the NUL character '\0', so the NUL character gets its own
+// test.
+TEST(StreamingUtf8ValidatorTest, NulIsValid) {
+ static const char kNul[] = "\x00";
+ EXPECT_EQ(VALID_ENDPOINT, StreamingUtf8Validator().AddBytes(kNul, 1));
+}
+
+// Just a basic sanity test before we start getting fancy.
+TEST(StreamingUtf8ValidatorTest, HelloWorld) {
+ static const char kHelloWorld[] = "Hello, World!";
+ EXPECT_EQ(
+ VALID_ENDPOINT,
+ StreamingUtf8Validator().AddBytes(kHelloWorld, strlen(kHelloWorld)));
+}
+
+// Check that the Reset() method works.
+TEST(StreamingUtf8ValidatorTest, ResetWorks) {
+ StreamingUtf8Validator validator;
+ EXPECT_EQ(INVALID, validator.AddBytes("\xC0", 1));
+ EXPECT_EQ(INVALID, validator.AddBytes("a", 1));
+ validator.Reset();
+ EXPECT_EQ(VALID_ENDPOINT, validator.AddBytes("a", 1));
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Valid) {
+ CheckRange(valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Partial) {
+ CheckRange(PartialIterator(), PartialIterator::end(), VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, Invalid) {
+ CheckRange(invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, ValidByByte) {
+ CheckRangeByteAtATime(valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, PartialByByte) {
+ CheckRangeByteAtATime(
+ PartialIterator(), PartialIterator::end(), VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorSingleSequenceTest, InvalidByByte) {
+ CheckRangeByteAtATime(invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusValidIsValid) {
+ CheckCombinations(valid, valid_end, valid, valid_end, VALID_ENDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusPartialIsPartial) {
+ CheckCombinations(valid,
+ valid_end,
+ PartialIterator(),
+ PartialIterator::end(),
+ VALID_MIDPOINT);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusValidIsInvalid) {
+ CheckCombinations(
+ PartialIterator(), PartialIterator::end(), valid, valid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusPartialIsInvalid) {
+ CheckCombinations(PartialIterator(),
+ PartialIterator::end(),
+ PartialIterator(),
+ PartialIterator::end(),
+ INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, ValidPlusInvalidIsInvalid) {
+ CheckCombinations(valid, valid_end, invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusValidIsInvalid) {
+ CheckCombinations(invalid, invalid_end, valid, valid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusInvalidIsInvalid) {
+ CheckCombinations(invalid, invalid_end, invalid, invalid_end, INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, InvalidPlusPartialIsInvalid) {
+ CheckCombinations(
+ invalid, invalid_end, PartialIterator(), PartialIterator::end(), INVALID);
+}
+
+TEST_F(StreamingUtf8ValidatorDoubleSequenceTest, PartialPlusInvalidIsInvalid) {
+ CheckCombinations(
+ PartialIterator(), PartialIterator::end(), invalid, invalid_end, INVALID);
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, EmptyIsValid) {
+ EXPECT_TRUE(StreamingUtf8Validator::Validate(std::string()));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, SimpleValidCase) {
+ EXPECT_TRUE(StreamingUtf8Validator::Validate("\xc2\x81"));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, SimpleInvalidCase) {
+ EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc0\x80"));
+}
+
+TEST(StreamingUtf8ValidatorValidateTest, TruncatedIsInvalid) {
+ EXPECT_FALSE(StreamingUtf8Validator::Validate("\xc2"));
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/i18n/utf8_validator_tables.cc b/chromium/base/i18n/utf8_validator_tables.cc
new file mode 100644
index 00000000000..8dfa10caf79
--- /dev/null
+++ b/chromium/base/i18n/utf8_validator_tables.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium 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 auto-generated by build_utf8_validator_tables.
+// DO NOT EDIT.
+
+#include "base/i18n/utf8_validator_tables.h"
+
+namespace base {
+namespace internal {
+
+const uint8 kUtf8ValidatorTables[] = {
+ // State 0, offset 0x00
+ 0x00, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x08
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x10
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x18
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x20
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x28
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x30
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x38
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x40
+ 0x81, 0x81, 0x81, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x48
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x50
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x58
+ 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, // 0x60
+ 0x83, 0x86, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, // 0x68
+ 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8b, 0x8e, 0x8b, // 0x70
+ 0x8b, 0x93, 0x9c, 0x9c, 0x9c, 0x9f, 0x81, 0x81, // 0x78
+ 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0x80
+ 0x81, // 0x81
+ // State 1, offset 0x81
+ 0x07, 0x81, // 0x83
+ // State 2, offset 0x83
+ 0x06, 0x00, 0x81, // 0x86
+ // State 3, offset 0x86
+ 0x05, 0x81, 0x83, 0x81, 0x81, // 0x8b
+ // State 4, offset 0x8b
+ 0x06, 0x83, 0x81, // 0x8e
+ // State 5, offset 0x8e
+ 0x05, 0x83, 0x81, 0x81, 0x81, // 0x93
+ // State 6, offset 0x93
+ 0x04, 0x81, 0x8b, 0x8b, 0x8b, 0x81, 0x81, 0x81, // 0x9b
+ 0x81, // 0x9c
+ // State 7, offset 0x9c
+ 0x06, 0x8b, 0x81, // 0x9f
+ // State 8, offset 0x9f
+ 0x04, 0x8b, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, // 0xa7
+ 0x81, // 0xa8
+};
+
+const size_t kUtf8ValidatorTablesSize = arraysize(kUtf8ValidatorTables);
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/i18n/utf8_validator_tables.h b/chromium/base/i18n/utf8_validator_tables.h
new file mode 100644
index 00000000000..b7db56e43e4
--- /dev/null
+++ b/chromium/base/i18n/utf8_validator_tables.h
@@ -0,0 +1,29 @@
+// Copyright 2014 The Chromium Authors. All 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_UTF8_VALIDATOR_TABLES_H_
+#define BASE_I18N_UTF8_VALIDATOR_TABLES_H_
+
+#include "base/basictypes.h"
+
+namespace base {
+namespace internal {
+
+// The tables for all states; a list of entries of the form (right_shift,
+// next_state, next_state, ....). The right_shifts are used to reduce the
+// overall size of the table. The table only covers bytes in the range
+// [0x80, 0xFF] to save space.
+extern const uint8 kUtf8ValidatorTables[];
+
+extern const size_t kUtf8ValidatorTablesSize;
+
+// The offset of the INVALID state in kUtf8ValidatorTables.
+enum {
+ I18N_UTF8_VALIDATOR_INVALID_INDEX = 129
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_I18N_UTF8_VALIDATOR_TABLES_H_
diff --git a/chromium/base/id_map.h b/chromium/base/id_map.h
index 27098d29768..9cbc1f8978f 100644
--- a/chromium/base/id_map.h
+++ b/chromium/base/id_map.h
@@ -32,8 +32,10 @@ enum IDMapOwnershipSemantics {
// ownership semantics are set to own because pointers will leak.
template<typename T, IDMapOwnershipSemantics OS = IDMapExternalPointer>
class IDMap : public base::NonThreadSafe {
- private:
+ public:
typedef int32 KeyType;
+
+ private:
typedef base::hash_map<KeyType, T*> HashTable;
public:
diff --git a/chromium/base/id_map_unittest.cc b/chromium/base/id_map_unittest.cc
index 76d7c3ed4e9..c005a690578 100644
--- a/chromium/base/id_map_unittest.cc
+++ b/chromium/base/id_map_unittest.cc
@@ -94,31 +94,41 @@ TEST(IDMapTest, IteratorRemainsValidWhenRemovingOtherElements) {
const int kCount = 5;
TestObject obj[kCount];
- int32 ids[kCount];
for (int i = 0; i < kCount; i++)
- ids[i] = map.Add(&obj[i]);
+ map.Add(&obj[i]);
+ // IDMap uses a hash_map, which has no predictable iteration order.
+ int32 ids_in_iteration_order[kCount];
+ const TestObject* objs_in_iteration_order[kCount];
int counter = 0;
for (IDMap<TestObject>::const_iterator iter(&map);
!iter.IsAtEnd(); iter.Advance()) {
+ ids_in_iteration_order[counter] = iter.GetCurrentKey();
+ objs_in_iteration_order[counter] = iter.GetCurrentValue();
+ counter++;
+ }
+
+ 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]);
+ EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey());
+ EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue());
+ map.Remove(ids_in_iteration_order[1]);
break;
case 1:
- EXPECT_EQ(ids[2], iter.GetCurrentKey());
- EXPECT_EQ(&obj[2], iter.GetCurrentValue());
- map.Remove(ids[3]);
+ EXPECT_EQ(ids_in_iteration_order[2], iter.GetCurrentKey());
+ EXPECT_EQ(objs_in_iteration_order[2], iter.GetCurrentValue());
+ map.Remove(ids_in_iteration_order[3]);
break;
case 2:
- EXPECT_EQ(ids[4], iter.GetCurrentKey());
- EXPECT_EQ(&obj[4], iter.GetCurrentValue());
- map.Remove(ids[0]);
+ EXPECT_EQ(ids_in_iteration_order[4], iter.GetCurrentKey());
+ EXPECT_EQ(objs_in_iteration_order[4], iter.GetCurrentValue());
+ map.Remove(ids_in_iteration_order[0]);
break;
default:
FAIL() << "should not have that many elements";
@@ -194,22 +204,32 @@ TEST(IDMapTest, IteratorRemainsValidWhenClearing) {
const int kCount = 5;
TestObject obj[kCount];
- int32 ids[kCount];
for (int i = 0; i < kCount; i++)
- ids[i] = map.Add(&obj[i]);
+ map.Add(&obj[i]);
+ // IDMap uses a hash_map, which has no predictable iteration order.
+ int32 ids_in_iteration_order[kCount];
+ const TestObject* objs_in_iteration_order[kCount];
int counter = 0;
for (IDMap<TestObject>::const_iterator iter(&map);
!iter.IsAtEnd(); iter.Advance()) {
+ ids_in_iteration_order[counter] = iter.GetCurrentKey();
+ objs_in_iteration_order[counter] = iter.GetCurrentValue();
+ counter++;
+ }
+
+ 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());
+ EXPECT_EQ(ids_in_iteration_order[0], iter.GetCurrentKey());
+ EXPECT_EQ(objs_in_iteration_order[0], iter.GetCurrentValue());
break;
case 1:
- EXPECT_EQ(ids[1], iter.GetCurrentKey());
- EXPECT_EQ(&obj[1], iter.GetCurrentValue());
+ EXPECT_EQ(ids_in_iteration_order[1], iter.GetCurrentKey());
+ EXPECT_EQ(objs_in_iteration_order[1], iter.GetCurrentValue());
map.Clear();
EXPECT_TRUE(map.IsEmpty());
EXPECT_EQ(0U, map.size());
diff --git a/chromium/base/ios/device_util.h b/chromium/base/ios/device_util.h
index a4fa4887d35..1cd9a043bc9 100644
--- a/chromium/base/ios/device_util.h
+++ b/chromium/base/ios/device_util.h
@@ -40,8 +40,21 @@ namespace device_util {
// x86_64 -> Simulator
std::string GetPlatform();
-// Returns true if the application is running on a high-ram device. (>=500M).
-bool IsRunningOnHighRamDevice();
+// Returns true if the application is running on a device with 512MB or more
+// RAM.
+bool RamIsAtLeast512Mb();
+
+// Returns true if the application is running on a device with 1024MB or more
+// RAM.
+bool RamIsAtLeast1024Mb();
+
+// Returns true if the application is running on a device with |ram_in_mb| MB or
+// more RAM.
+// Use with caution! Actual RAM reported by devices is less than the commonly
+// used powers-of-two values. For example, a 512MB device may report only 502MB
+// RAM. The convenience methods above should be used in most cases because they
+// correctly handle this issue.
+bool RamIsAtLeast(uint64_t ram_in_mb);
// Returns true if the device has only one core.
bool IsSingleCoreDevice();
@@ -59,6 +72,12 @@ std::string GetRandomId();
// something that should be anonymous, you should probably pass NULL.
std::string GetDeviceIdentifier(const char* salt);
+// Returns a hashed version of |in_string| using |salt| (which must not be
+// zero-length). Different salt values should result in differently hashed
+// strings.
+std::string GetSaltedString(const std::string& in_string,
+ const std::string& salt);
+
} // namespace device_util
} // namespace ios
diff --git a/chromium/base/ios/device_util.mm b/chromium/base/ios/device_util.mm
index 0d516f9a2b4..ff7be36875c 100644
--- a/chromium/base/ios/device_util.mm
+++ b/chromium/base/ios/device_util.mm
@@ -44,13 +44,9 @@ NSString* GenerateClientId() {
// 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 = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
+ if ([client_id isEqualToString:kZeroUUID])
client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId());
- }
}
return client_id;
}
@@ -68,12 +64,22 @@ std::string GetPlatform() {
return platform;
}
-bool IsRunningOnHighRamDevice() {
+bool RamIsAtLeast512Mb() {
+ // 512MB devices report anywhere from 502-504 MB, use 450 MB just to be safe.
+ return RamIsAtLeast(450);
+}
+
+bool RamIsAtLeast1024Mb() {
+ // 1GB devices report anywhere from 975-999 MB, use 900 MB just to be safe.
+ return RamIsAtLeast(900);
+}
+
+bool RamIsAtLeast(uint64_t ram_in_mb) {
uint64_t memory_size = 0;
size_t size = sizeof(memory_size);
if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) {
// Anything >= 500M, call high ram.
- return memory_size >= 500 * 1024 * 1024;
+ return memory_size >= ram_in_mb * 1024 * 1024;
}
return false;
}
@@ -147,8 +153,15 @@ std::string GetDeviceIdentifier(const char* salt) {
[defaults synchronize];
}
- NSData* hash_data = [[NSString stringWithFormat:@"%@%s", client_id,
- salt ? salt : kDefaultSalt] dataUsingEncoding:NSUTF8StringEncoding];
+ return GetSaltedString(base::SysNSStringToUTF8(client_id),
+ salt ? salt : kDefaultSalt);
+}
+
+std::string GetSaltedString(const std::string& in_string,
+ const std::string& salt) {
+ DCHECK(salt.length());
+ NSData* hash_data = [base::SysUTF8ToNSString(in_string + salt)
+ dataUsingEncoding:NSUTF8StringEncoding];
unsigned char hash[CC_SHA256_DIGEST_LENGTH];
CC_SHA256([hash_data bytes], [hash_data length], hash);
diff --git a/chromium/base/ios/device_util_unittest.mm b/chromium/base/ios/device_util_unittest.mm
index c2aefe0f737..3494e00a73e 100644
--- a/chromium/base/ios/device_util_unittest.mm
+++ b/chromium/base/ios/device_util_unittest.mm
@@ -30,10 +30,6 @@ 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();
}
@@ -56,8 +52,7 @@ TEST_F(DeviceUtilTest, GetDeviceIdentifier) {
CleanNSUserDefaultsForDeviceId();
std::string new_default_id = ios::device_util::GetDeviceIdentifier(NULL);
- if (base::ios::IsRunningOnIOS6OrLater() &&
- ![[[[UIDevice currentDevice] identifierForVendor] UUIDString]
+ if (![[[[UIDevice currentDevice] identifierForVendor] UUIDString]
isEqualToString:@"00000000-0000-0000-0000-000000000000"]) {
EXPECT_EQ(default_id, new_default_id);
} else {
@@ -103,6 +98,33 @@ TEST_F(DeviceUtilTest, CheckMigrationFromZero) {
CleanNSUserDefaultsForDeviceId();
}
+TEST_F(DeviceUtilTest, GetSaltedStringEquals) {
+ std::string string1("The quick brown fox jumps over the lazy dog");
+ std::string string2("The quick brown fox jumps over the lazy dog");
+ std::string salt("salt");
+ // Same string and same salt should result in the same salted string.
+ EXPECT_EQ(ios::device_util::GetSaltedString(string1, salt),
+ ios::device_util::GetSaltedString(string2, salt));
+}
+
+TEST_F(DeviceUtilTest, GetSaltedStringNotEquals) {
+ std::string string1("The quick brown fox jumps over the lazy dog");
+ std::string string2("The lazy brown fox jumps over the quick dog");
+ std::string salt("salt");
+ // Different string and same salt should result in different salted strings.
+ EXPECT_NE(ios::device_util::GetSaltedString(string1, salt),
+ ios::device_util::GetSaltedString(string2, salt));
+}
+
+TEST_F(DeviceUtilTest, GetSaltedStringDifferentSalt) {
+ std::string string1("The quick brown fox jumps over the lazy dog");
+ std::string salt1("salt");
+ std::string salt2("pepper");
+ // Same string with different salt should result in different salted strings.
+ EXPECT_NE(ios::device_util::GetSaltedString(string1, salt1),
+ ios::device_util::GetSaltedString(string1, salt2));
+}
+
TEST_F(DeviceUtilTest, CheckDeviceMigration) {
CleanNSUserDefaultsForDeviceId();
diff --git a/chromium/base/ios/ios_util.h b/chromium/base/ios/ios_util.h
index 7e2e621dfdb..f9ddb262592 100644
--- a/chromium/base/ios/ios_util.h
+++ b/chromium/base/ios/ios_util.h
@@ -11,9 +11,6 @@
namespace base {
namespace ios {
-// Returns whether the operating system is iOS 6 or later.
-BASE_EXPORT bool IsRunningOnIOS6OrLater();
-
// Returns whether the operating system is iOS 7 or later.
BASE_EXPORT bool IsRunningOnIOS7OrLater();
diff --git a/chromium/base/ios/ios_util.mm b/chromium/base/ios/ios_util.mm
index a76911017f9..0f106de566d 100644
--- a/chromium/base/ios/ios_util.mm
+++ b/chromium/base/ios/ios_util.mm
@@ -20,10 +20,6 @@ const int32* OSVersionAsArray() {
namespace base {
namespace ios {
-bool IsRunningOnIOS6OrLater() {
- return IsRunningOnOrLater(6, 0, 0);
-}
-
bool IsRunningOnIOS7OrLater() {
return IsRunningOnOrLater(7, 0, 0);
}
diff --git a/chromium/base/json/json_file_value_serializer.cc b/chromium/base/json/json_file_value_serializer.cc
index 70d0c88f3cc..e33080ccd6b 100644
--- a/chromium/base/json/json_file_value_serializer.cc
+++ b/chromium/base/json/json_file_value_serializer.cc
@@ -36,9 +36,8 @@ bool JSONFileValueSerializer::SerializeInternal(const base::Value& root,
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)
+ if (base::WriteFile(json_file_path_, json_string.data(), data_size) !=
+ data_size)
return false;
return true;
diff --git a/chromium/base/json/json_string_value_serializer.cc b/chromium/base/json/json_string_value_serializer.cc
index 7611fbeed65..59f030dff2f 100644
--- a/chromium/base/json/json_string_value_serializer.cc
+++ b/chromium/base/json/json_string_value_serializer.cc
@@ -32,8 +32,7 @@ bool JSONStringValueSerializer::SerializeInternal(const Value& root,
if (pretty_print_)
options |= base::JSONWriter::OPTIONS_PRETTY_PRINT;
- base::JSONWriter::WriteWithOptions(&root, options, json_string_);
- return true;
+ return base::JSONWriter::WriteWithOptions(&root, options, json_string_);
}
Value* JSONStringValueSerializer::Deserialize(int* error_code,
diff --git a/chromium/base/json/json_value_serializer_unittest.cc b/chromium/base/json/json_value_serializer_unittest.cc
index 44c0a57cd27..f8d3a20109a 100644
--- a/chromium/base/json/json_value_serializer_unittest.cc
+++ b/chromium/base/json/json_value_serializer_unittest.cc
@@ -118,7 +118,7 @@ TEST(JSONValueSerializerTest, ReadProperJSONFromFile) {
// 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)));
+ WriteFile(temp_file, kProperJSON, strlen(kProperJSON)));
// Try to deserialize it through the serializer.
JSONFileValueSerializer file_deserializer(temp_file);
@@ -142,9 +142,8 @@ TEST(JSONValueSerializerTest, ReadJSONWithCommasFromFile) {
// 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)));
+ WriteFile(temp_file, kProperJSONWithCommas,
+ strlen(kProperJSONWithCommas)));
// Try to deserialize it through the serializer.
JSONFileValueSerializer file_deserializer(temp_file);
diff --git a/chromium/base/json/json_writer.cc b/chromium/base/json/json_writer.cc
index d6006638219..d14c92c06e5 100644
--- a/chromium/base/json/json_writer.cc
+++ b/chromium/base/json/json_writer.cc
@@ -9,211 +9,199 @@
#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
-#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
namespace base {
#if defined(OS_WIN)
-static const char kPrettyPrintLineEnding[] = "\r\n";
+const char kPrettyPrintLineEnding[] = "\r\n";
#else
-static const char kPrettyPrintLineEnding[] = "\n";
+const char kPrettyPrintLineEnding[] = "\n";
#endif
// static
-void JSONWriter::Write(const Value* const node, std::string* json) {
- WriteWithOptions(node, 0, json);
+bool JSONWriter::Write(const Value* const node, std::string* json) {
+ return WriteWithOptions(node, 0, json);
}
// static
-void JSONWriter::WriteWithOptions(const Value* const node, int options,
+bool 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 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(options, json);
+ bool result = writer.BuildJSONString(node, 0U);
- JSONWriter writer(omit_binary_values, omit_double_type_preservation,
- pretty_print, json);
- writer.BuildJSONString(node, 0);
-
- if (pretty_print)
+ if (options & OPTIONS_PRETTY_PRINT)
json->append(kPrettyPrintLineEnding);
+
+ return result;
}
-JSONWriter::JSONWriter(bool omit_binary_values,
- bool omit_double_type_preservation, bool pretty_print,
- std::string* json)
- : omit_binary_values_(omit_binary_values),
- omit_double_type_preservation_(omit_double_type_preservation),
- pretty_print_(pretty_print),
+JSONWriter::JSONWriter(int options, std::string* json)
+ : omit_binary_values_((options & OPTIONS_OMIT_BINARY_VALUES) != 0),
+ omit_double_type_preservation_(
+ (options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION) != 0),
+ pretty_print_((options & OPTIONS_PRETTY_PRINT) != 0),
json_string_(json) {
DCHECK(json);
}
-void JSONWriter::BuildJSONString(const Value* const node, int depth) {
+bool JSONWriter::BuildJSONString(const Value* const node, size_t depth) {
switch (node->GetType()) {
- case Value::TYPE_NULL:
+ 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;
+ return true;
+ }
+
+ case Value::TYPE_BOOLEAN: {
+ bool value;
+ bool result = node->GetAsBoolean(&value);
+ DCHECK(result);
+ json_string_->append(value ? "true" : "false");
+ return result;
+ }
+
+ case Value::TYPE_INTEGER: {
+ int value;
+ bool result = node->GetAsInteger(&value);
+ DCHECK(result);
+ json_string_->append(IntToString(value));
+ return result;
+ }
+
+ 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)));
+ return result;
}
-
- 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;
+ 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");
}
-
- case Value::TYPE_STRING:
- {
- std::string value;
- bool result = node->GetAsString(&value);
- DCHECK(result);
- EscapeJSONString(value, true, json_string_);
- break;
+ // 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(static_cast<size_t>(0), static_cast<size_t>(1), '0');
+ } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') {
+ // "-.1" bad "-0.1" good
+ real.insert(static_cast<size_t>(1), static_cast<size_t>(1), '0');
}
+ json_string_->append(real);
+ return result;
+ }
+
+ case Value::TYPE_STRING: {
+ std::string value;
+ bool result = node->GetAsString(&value);
+ DCHECK(result);
+ EscapeJSONString(value, true, json_string_);
+ return result;
+ }
+
+ case Value::TYPE_LIST: {
+ json_string_->push_back('[');
+ if (pretty_print_)
+ json_string_->push_back(' ');
+
+ const ListValue* list = NULL;
+ bool first_value_has_been_output = false;
+ bool result = node->GetAsList(&list);
+ DCHECK(result);
+ for (ListValue::const_iterator it = list->begin(); it != list->end();
+ ++it) {
+ const Value* value = *it;
+ if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY)
+ continue;
+
+ if (first_value_has_been_output) {
+ json_string_->push_back(',');
+ if (pretty_print_)
+ json_string_->push_back(' ');
+ }
- 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 (!BuildJSONString(value, depth))
+ result = false;
- if (omit_binary_values_ && value->GetType() == Value::TYPE_BINARY) {
- continue;
- }
+ first_value_has_been_output = true;
+ }
- if (i != 0) {
- json_string_->append(",");
- if (pretty_print_)
- json_string_->append(" ");
- }
+ if (pretty_print_)
+ json_string_->push_back(' ');
+ json_string_->push_back(']');
+ return result;
+ }
+
+ case Value::TYPE_DICTIONARY: {
+ json_string_->push_back('{');
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+
+ const DictionaryValue* dict = NULL;
+ bool first_value_has_been_output = false;
+ bool result = node->GetAsDictionary(&dict);
+ DCHECK(result);
+ for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
+ itr.Advance()) {
+ if (omit_binary_values_ &&
+ itr.value().GetType() == Value::TYPE_BINARY) {
+ continue;
+ }
- BuildJSONString(value, depth);
+ if (first_value_has_been_output) {
+ json_string_->push_back(',');
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
}
if (pretty_print_)
- json_string_->append(" ");
- json_string_->append("]");
- break;
- }
+ IndentLine(depth + 1U);
- case Value::TYPE_DICTIONARY:
- {
- json_string_->append("{");
+ EscapeJSONString(itr.key(), true, json_string_);
+ json_string_->push_back(':');
if (pretty_print_)
- json_string_->append(kPrettyPrintLineEnding);
-
- const DictionaryValue* dict =
- static_cast<const DictionaryValue*>(node);
- bool first_entry = true;
- for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
- itr.Advance(), first_entry = false) {
- if (omit_binary_values_ &&
- itr.value().GetType() == Value::TYPE_BINARY) {
- continue;
- }
-
- if (!first_entry) {
- json_string_->append(",");
- if (pretty_print_)
- json_string_->append(kPrettyPrintLineEnding);
- }
+ json_string_->push_back(' ');
- if (pretty_print_)
- IndentLine(depth + 1);
-
- EscapeJSONString(itr.key(), true, json_string_);
- if (pretty_print_) {
- json_string_->append(": ");
- } else {
- json_string_->append(":");
- }
- BuildJSONString(&itr.value(), depth + 1);
- }
+ if (!BuildJSONString(&itr.value(), depth + 1U))
+ result = false;
- if (pretty_print_) {
- json_string_->append(kPrettyPrintLineEnding);
- IndentLine(depth);
- json_string_->append("}");
- } else {
- json_string_->append("}");
- }
- break;
+ first_value_has_been_output = true;
}
- case Value::TYPE_BINARY:
- {
- if (!omit_binary_values_) {
- NOTREACHED() << "Cannot serialize binary value.";
- }
- break;
+ if (pretty_print_) {
+ json_string_->append(kPrettyPrintLineEnding);
+ IndentLine(depth);
}
- default:
- NOTREACHED() << "unknown json type";
+ json_string_->push_back('}');
+ return result;
+ }
+
+ case Value::TYPE_BINARY:
+ // Successful only if we're allowed to omit it.
+ DLOG_IF(ERROR, !omit_binary_values_) << "Cannot serialize binary value.";
+ return omit_binary_values_;
}
+ NOTREACHED();
+ return false;
}
-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, ' '));
+void JSONWriter::IndentLine(size_t depth) {
+ json_string_->append(depth * 3U, ' ');
}
} // namespace base
diff --git a/chromium/base/json/json_writer.h b/chromium/base/json/json_writer.h
index e4a143c7780..9709c7e723f 100644
--- a/chromium/base/json/json_writer.h
+++ b/chromium/base/json/json_writer.h
@@ -17,8 +17,10 @@ class Value;
class BASE_EXPORT JSONWriter {
public:
enum Options {
- // For values of binary type, the value (and key if within a dictionary)
- // will be omitted from the output.
+ // This option instructs the writer that if a Binary value is encountered,
+ // the value (and key if within a dictionary) will be omitted from the
+ // output, and success will be returned. Otherwise, if a binary value is
+ // encountered, failure will be returned.
OPTIONS_OMIT_BINARY_VALUES = 1 << 0,
// This option instructs the writer to write doubles that have no fractional
@@ -35,25 +37,23 @@ class BASE_EXPORT JSONWriter {
// 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);
+ // values)? Return true on success and false on failure.
+ static bool 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,
+ // bitwise ORed together. Return true on success and false on failure.
+ static bool WriteWithOptions(const Value* const node, int options,
std::string* json);
private:
- JSONWriter(bool omit_binary_values,
- bool omit_double_type_preservation, bool pretty_print,
- std::string* json);
+ JSONWriter(int options, 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);
+ // Called recursively to build the JSON string. When completed,
+ // |json_string_| will contain the JSON.
+ bool BuildJSONString(const Value* const node, size_t depth);
// Adds space to json_string_ for the indent level.
- void IndentLine(int depth);
+ void IndentLine(size_t depth);
bool omit_binary_values_;
bool omit_double_type_preservation_;
diff --git a/chromium/base/json/json_writer_unittest.cc b/chromium/base/json/json_writer_unittest.cc
index 7ddd7b462d6..ae46800dde6 100644
--- a/chromium/base/json/json_writer_unittest.cc
+++ b/chromium/base/json/json_writer_unittest.cc
@@ -8,44 +8,68 @@
namespace base {
-TEST(JSONWriterTest, Writing) {
- // Test null
- Value* root = Value::CreateNullValue();
+TEST(JSONWriterTest, BasicTypes) {
std::string output_js;
- JSONWriter::Write(root, &output_js);
- ASSERT_EQ("null", output_js);
+
+ // Test null.
+ Value* root = Value::CreateNullValue();
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("null", output_js);
delete root;
- // Test empty dict
+ // Test empty dict.
root = new DictionaryValue;
- JSONWriter::Write(root, &output_js);
- ASSERT_EQ("{}", output_js);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("{}", output_js);
delete root;
- // Test empty list
+ // Test empty list.
root = new ListValue;
- JSONWriter::Write(root, &output_js);
- ASSERT_EQ("[]", output_js);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("[]", output_js);
+ delete root;
+
+ // Test integer values.
+ root = new FundamentalValue(42);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("42", output_js);
+ delete root;
+
+ // Test boolean values.
+ root = new FundamentalValue(true);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("true", 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);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_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);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_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);
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("-0.8", output_js);
delete root;
+ // Test String values.
+ root = new StringValue("foo");
+ EXPECT_TRUE(JSONWriter::Write(root, &output_js));
+ EXPECT_EQ("\"foo\"", output_js);
+ delete root;
+}
+
+
+TEST(JSONWriterTest, NestedTypes) {
+ std::string output_js;
+
// Writer unittests like empty list/dict nesting,
// list list nesting, etc.
DictionaryValue root_dict;
@@ -59,10 +83,12 @@ TEST(JSONWriterTest, Writing) {
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);
+ EXPECT_TRUE(JSONWriter::Write(&root_dict, &output_js));
+ EXPECT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js);
+ EXPECT_TRUE(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)
@@ -70,62 +96,79 @@ TEST(JSONWriterTest, Writing) {
#else
#define JSON_NEWLINE "\n"
#endif
- ASSERT_EQ("{" JSON_NEWLINE
+ EXPECT_EQ("{" JSON_NEWLINE
" \"list\": [ {" JSON_NEWLINE
" \"inner int\": 10" JSON_NEWLINE
" }, [ ], true ]" JSON_NEWLINE
"}" JSON_NEWLINE,
output_js);
#undef JSON_NEWLINE
+}
+
+TEST(JSONWriterTest, KeysWithPeriods) {
+ std::string output_js;
- // 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);
+ EXPECT_TRUE(JSONWriter::Write(&period_dict, &output_js));
+ EXPECT_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());
+ EXPECT_TRUE(JSONWriter::Write(&period_dict3, &output_js));
+ EXPECT_EQ("{\"a\":{\"b\":2},\"a.b\":1}", output_js);
+}
+
+TEST(JSONWriterTest, BinaryValues) {
+ std::string output_js;
+
+ // Binary values should return errors unless suppressed via the
+ // OPTIONS_OMIT_BINARY_VALUES flag.
+ Value* root = BinaryValue::CreateWithCopiedBuffer("asdf", 4);
+ EXPECT_FALSE(JSONWriter::Write(root, &output_js));
+ EXPECT_TRUE(JSONWriter::WriteWithOptions(
+ root, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js));
+ EXPECT_TRUE(output_js.empty());
delete root;
ListValue binary_list;
+ binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
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);
+ binary_list.Append(BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ EXPECT_FALSE(JSONWriter::Write(&binary_list, &output_js));
+ EXPECT_TRUE(JSONWriter::WriteWithOptions(
+ &binary_list, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js));
+ EXPECT_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);
+ binary_dict.Set("a", BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ binary_dict.Set("b", new FundamentalValue(5));
+ binary_dict.Set("c", BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ binary_dict.Set("d", new FundamentalValue(2));
+ binary_dict.Set("e", BinaryValue::CreateWithCopiedBuffer("asdf", 4));
+ EXPECT_FALSE(JSONWriter::Write(&binary_dict, &output_js));
+ EXPECT_TRUE(JSONWriter::WriteWithOptions(
+ &binary_dict, JSONWriter::OPTIONS_OMIT_BINARY_VALUES, &output_js));
+ EXPECT_EQ("{\"b\":5,\"d\":2}", output_js);
+}
+
+TEST(JSONWriterTest, DoublesAsInts) {
+ std::string output_js;
// Test allowing a double with no fractional part to be written as an integer.
FundamentalValue double_value(1e10);
- JSONWriter::WriteWithOptions(
+ EXPECT_TRUE(JSONWriter::WriteWithOptions(
&double_value,
JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION,
- &output_js);
- ASSERT_EQ("10000000000", output_js);
+ &output_js));
+ EXPECT_EQ("10000000000", output_js);
}
} // namespace base
diff --git a/chromium/base/lazy_instance.h b/chromium/base/lazy_instance.h
index 3935780a55b..05a7c5d81ec 100644
--- a/chromium/base/lazy_instance.h
+++ b/chromium/base/lazy_instance.h
@@ -57,7 +57,9 @@ namespace base {
template <typename Type>
struct DefaultLazyInstanceTraits {
static const bool kRegisterOnExit = true;
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = false;
+#endif
static Type* New(void* instance) {
DCHECK_EQ(reinterpret_cast<uintptr_t>(instance) & (ALIGNOF(Type) - 1), 0u)
@@ -89,7 +91,9 @@ namespace internal {
template <typename Type>
struct LeakyLazyInstanceTraits {
static const bool kRegisterOnExit = false;
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
static Type* New(void* instance) {
ANNOTATE_SCOPED_MEMORY_LEAK;
diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc
index f8dd2d0088b..36710f28137 100644
--- a/chromium/base/linux_util.cc
+++ b/chromium/base/linux_util.cc
@@ -71,61 +71,10 @@ class LinuxDistroHelper {
};
#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;
@@ -176,82 +125,10 @@ std::string GetLinuxDistro() {
void SetLinuxDistro(const std::string& distro) {
std::string trimmed_distro;
- TrimWhitespaceASCII(distro, TRIM_ALL, &trimmed_distro);
+ base::TrimWhitespaceASCII(distro, base::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 = static_cast<ino_t>(-1);
- 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];
diff --git a/chromium/base/linux_util.h b/chromium/base/linux_util.h
index b9ba56dfc97..83523df831d 100644
--- a/chromium/base/linux_util.h
+++ b/chromium/base/linux_util.h
@@ -14,8 +14,6 @@
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[];
@@ -26,13 +24,6 @@ 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|.
diff --git a/chromium/base/location.cc b/chromium/base/location.cc
index b5da027ee82..08263cebaad 100644
--- a/chromium/base/location.cc
+++ b/chromium/base/location.cc
@@ -92,11 +92,11 @@ __declspec(noinline)
BASE_EXPORT const void* GetProgramCounter() {
#if defined(COMPILER_MSVC)
return _ReturnAddress();
-#elif defined(COMPILER_GCC)
+#elif defined(COMPILER_GCC) && !defined(OS_NACL)
return __builtin_extract_return_addr(__builtin_return_address(0));
-#endif // COMPILER_GCC
-
+#else
return NULL;
+#endif
}
} // namespace tracked_objects
diff --git a/chromium/base/logging.cc b/chromium/base/logging.cc
index e836092e766..b2938f31144 100644
--- a/chromium/base/logging.cc
+++ b/chromium/base/logging.cc
@@ -43,6 +43,7 @@ typedef pthread_mutex_t* MutexHandle;
#include <ctime>
#include <iomanip>
#include <ostream>
+#include <string>
#include "base/base_switches.h"
#include "base/command_line.h"
@@ -51,6 +52,8 @@ typedef pthread_mutex_t* MutexHandle;
#include "base/debug/stack_trace.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock_impl.h"
#include "base/threading/platform_thread.h"
@@ -65,23 +68,20 @@ typedef pthread_mutex_t* MutexHandle;
namespace logging {
-DcheckState g_dcheck_state = DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS;
-
-DcheckState get_dcheck_state() {
- return g_dcheck_state;
-}
-
-void set_dcheck_state(DcheckState state) {
- g_dcheck_state = state;
-}
-
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" };
+ "INFO", "WARNING", "ERROR", "FATAL" };
+
+const char* log_severity_name(int severity)
+{
+ if (severity >= 0 && severity < LOG_NUM_SEVERITIES)
+ return log_severity_names[severity];
+ return "UNKNOWN";
+}
int min_log_level = 0;
@@ -115,9 +115,6 @@ 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;
@@ -355,15 +352,13 @@ LoggingSettings::LoggingSettings()
: logging_dest(LOG_DEFAULT),
log_file(NULL),
lock_log(LOCK_LOG_FILE),
- delete_old(APPEND_TO_OLD_LOG_FILE),
- dcheck_state(DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) {}
+ delete_old(APPEND_TO_OLD_LOG_FILE) {}
bool BaseInitLoggingImpl(const LoggingSettings& settings) {
#if defined(OS_NACL)
// Can log only to the system debug log.
CHECK_EQ(settings.logging_dest & ~LOG_TO_SYSTEM_DEBUG_LOG, 0);
#endif
- g_dcheck_state = settings.dcheck_state;
CommandLine* command_line = CommandLine::ForCurrentProcess();
// Don't bother initializing g_vlog_info unless we use one of the
// vlog switches.
@@ -404,7 +399,7 @@ bool BaseInitLoggingImpl(const LoggingSettings& settings) {
}
void SetMinLogLevel(int level) {
- min_log_level = std::min(LOG_ERROR_REPORT, level);
+ min_log_level = std::min(LOG_FATAL, level);
}
int GetMinLogLevel() {
@@ -441,10 +436,6 @@ void SetLogAssertHandler(LogAssertHandlerFunction handler) {
log_assert_handler = handler;
}
-void SetLogReportHandler(LogReportHandlerFunction handler) {
- log_report_handler = handler;
-}
-
void SetLogMessageHandler(LogMessageHandlerFunction handler) {
log_message_handler = handler;
}
@@ -468,6 +459,7 @@ template std::string* MakeCheckOpString<std::string, std::string>(
const std::string&, const std::string&, const char* name);
#endif
+#if !defined(NDEBUG)
// 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
@@ -494,7 +486,7 @@ void DisplayDebugMessageInDialog(const std::string& str) {
backslash[1] = 0;
wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
- std::wstring cmdline = UTF8ToWide(str);
+ std::wstring cmdline = base::UTF8ToWide(str);
if (cmdline.empty())
return;
@@ -518,6 +510,7 @@ void DisplayDebugMessageInDialog(const std::string& str) {
// You can just look at stderr.
#endif
}
+#endif // !defined(NDEBUG)
#if defined(OS_WIN)
LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {
@@ -528,17 +521,6 @@ LogMessage::SaveLastError::~SaveLastError() {
}
#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);
@@ -560,7 +542,7 @@ LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
}
LogMessage::~LogMessage() {
-#if !defined(NDEBUG) && !defined(OS_NACL)
+#if !defined(NDEBUG) && !defined(OS_NACL) && !defined(__UCLIBC__)
if (severity_ == LOG_FATAL) {
// Include a stack trace on a fatal.
base::debug::StackTrace trace;
@@ -593,7 +575,6 @@ LogMessage::~LogMessage() {
priority = ANDROID_LOG_WARN;
break;
case LOG_ERROR:
- case LOG_ERROR_REPORT:
priority = ANDROID_LOG_ERROR;
break;
case LOG_FATAL:
@@ -602,13 +583,13 @@ LogMessage::~LogMessage() {
}
__android_log_write(priority, "chromium", str_newline.c_str());
#endif
- fprintf(stderr, "%s", str_newline.c_str());
+ ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
fflush(stderr);
} 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());
+ ignore_result(fwrite(str_newline.data(), str_newline.size(), 1, stderr));
fflush(stderr);
}
@@ -633,7 +614,8 @@ LogMessage::~LogMessage() {
&num_written,
NULL);
#else
- fprintf(log_file, "%s", str_newline.c_str());
+ ignore_result(fwrite(
+ str_newline.data(), str_newline.size(), 1, log_file));
fflush(log_file);
#endif
}
@@ -646,32 +628,20 @@ LogMessage::~LogMessage() {
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();
+ if (log_assert_handler) {
+ // Make a copy of the string for the handler out of paranoia.
+ log_assert_handler(std::string(stream_.str()));
} 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.
+ // 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());
+#endif
+ // Crash the process to generate a dump.
+ base::debug::BreakDebugger();
}
}
}
@@ -711,7 +681,7 @@ void LogMessage::Init(const char* file, int line) {
if (log_tickcount)
stream_ << TickCount() << ':';
if (severity_ >= 0)
- stream_ << log_severity_names[severity_];
+ stream_ << log_severity_name(severity_);
else
stream_ << "VERBOSE" << -severity_;
@@ -738,61 +708,40 @@ SystemErrorCode GetLastSystemErrorCode() {
}
#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) {
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) {
+ const int error_message_buffer_size = 256;
+ char msgbuf[error_message_buffer_size];
+ DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ DWORD len = FormatMessageA(flags, NULL, error_code, 0, msgbuf,
+ arraysize(msgbuf), NULL);
+ if (len) {
+ // Messages returned by system end with line breaks.
+ return base::CollapseWhitespaceASCII(msgbuf, true) +
+ base::StringPrintf(" (0x%X)", error_code);
+ }
+ return base::StringPrintf("Error (0x%X) while retrieving error. (0x%X)",
+ GetLastError(), error_code);
+}
+#elif defined(OS_POSIX)
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) {
+ return safe_strerror(error_code);
}
+#else
+#error Not implemented
+#endif
+
+#if defined(OS_WIN)
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_;
- }
+ stream() << ": " << SystemErrorCodeToString(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_;
@@ -808,7 +757,7 @@ ErrnoLogMessage::ErrnoLogMessage(const char* file,
}
ErrnoLogMessage::~ErrnoLogMessage() {
- stream() << ": " << safe_strerror(err_);
+ stream() << ": " << SystemErrorCodeToString(err_);
}
#endif // OS_WIN
@@ -862,5 +811,5 @@ std::wstring GetLogFileFullPath() {
} // namespace logging
std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
- return out << WideToUTF8(std::wstring(wstr));
+ return out << base::WideToUTF8(std::wstring(wstr));
}
diff --git a/chromium/base/logging.h b/chromium/base/logging.h
index 71f391f8aff..1a110a565e9 100644
--- a/chromium/base/logging.h
+++ b/chromium/base/logging.h
@@ -52,10 +52,6 @@
//
// 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.
@@ -131,18 +127,13 @@
// 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.
+// are (in increasing order of severity) INFO, WARNING, ERROR, 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.
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
namespace logging {
@@ -175,7 +166,7 @@ enum LoggingDestination {
// Indicates that the log file should be locked when being written to.
// Unless there is only one single-threaded process that is logging to
// the log file, the file should be locked during writes to make each
-// log outut atomic. Other writers will block.
+// log output atomic. Other writers will block.
//
// All processes writing to the log file must have their locking set for it to
// work properly. Defaults to LOCK_LOG_FILE.
@@ -185,11 +176,6 @@ enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
// 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
-};
-
struct BASE_EXPORT LoggingSettings {
// The defaults values are:
//
@@ -197,7 +183,6 @@ struct BASE_EXPORT LoggingSettings {
// log_file: NULL
// lock_log: LOCK_LOG_FILE
// delete_old: APPEND_TO_OLD_LOG_FILE
- // dcheck_state: DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS
LoggingSettings();
LoggingDestination logging_dest;
@@ -207,8 +192,6 @@ struct BASE_EXPORT LoggingSettings {
const PathChar* log_file;
LogLockingState lock_log;
OldFileDeletionState delete_old;
-
- DcheckState dcheck_state;
};
// Define different names for the BaseInitLoggingImpl() function depending on
@@ -288,13 +271,6 @@ BASE_EXPORT void SetShowErrorDialogs(bool enable_dialogs);
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
@@ -311,9 +287,8 @@ const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
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;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
// LOG_DFATAL is LOG_FATAL in debug mode, ERROR in normal mode
#ifdef NDEBUG
@@ -331,9 +306,6 @@ const LogSeverity LOG_DFATAL = LOG_FATAL;
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, ...) \
@@ -345,8 +317,6 @@ const LogSeverity LOG_DFATAL = LOG_FATAL;
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 \
@@ -366,10 +336,9 @@ const LogSeverity LOG_DFATAL = LOG_FATAL;
const LogSeverity LOG_0 = LOG_ERROR;
#endif
-// 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.
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. 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())
@@ -438,29 +407,13 @@ const LogSeverity LOG_0 = LOG_ERROR;
SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
#if defined(OS_WIN)
-#define LOG_GETLASTERROR_STREAM(severity) \
+#define PLOG_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) \
+#define PLOG_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)
#endif
#define PLOG(severity) \
@@ -469,20 +422,6 @@ const LogSeverity LOG_0 = LOG_ERROR;
#define PLOG_IF(severity, condition) \
LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
-#if !defined(NDEBUG)
-// Debug builds always include DCHECK and DLOG.
-#undef LOGGING_IS_OFFICIAL_BUILD
-#define LOGGING_IS_OFFICIAL_BUILD 0
-#elif defined(OFFICIAL_BUILD)
-// Official release builds always disable and remove DCHECK and DLOG.
-#undef LOGGING_IS_OFFICIAL_BUILD
-#define LOGGING_IS_OFFICIAL_BUILD 1
-#elif !defined(LOGGING_IS_OFFICIAL_BUILD)
-// Unless otherwise specified, unofficial release builds include
-// DCHECK and DLOG.
-#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)
@@ -494,10 +433,10 @@ const LogSeverity LOG_0 = LOG_ERROR;
// We make sure CHECK et al. always evaluates their arguments, as
// doing CHECK(FunctionWithSideEffect()) is a common idiom.
-#if LOGGING_IS_OFFICIAL_BUILD
+#if defined(OFFICIAL_BUILD) && defined(NDEBUG) && !defined(OS_ANDROID)
// Make all CHECK functions discard their log strings to reduce code
-// bloat for official builds.
+// bloat for official release builds.
// TODO(akalin): This would be more valuable if there were some way to
// remove BreakDebugger() from the backtrace, perhaps by turning it
@@ -594,22 +533,16 @@ DEFINE_CHECK_OP_IMPL(GT, > )
#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
-// In order to have optimized code for official builds, remove DLOGs and
-// DCHECKs.
-#define ENABLE_DLOG 0
-#define ENABLE_DCHECK 0
-
-#elif defined(NDEBUG)
-// Otherwise, if we're a release build, remove DLOGs but not DCHECKs
-// (since those can still be turned on via a command-line flag).
+#if defined(NDEBUG)
#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
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON 0
+#else
+#define DCHECK_IS_ON 1
#endif
// Definitions for DLOG et al.
@@ -654,17 +587,6 @@ enum { DEBUG_MODE = 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))
@@ -674,75 +596,40 @@ enum { DEBUG_MODE = ENABLE_DLOG };
// Definitions for DCHECK et al.
-#if ENABLE_DCHECK
-
-#if defined(NDEBUG)
-
-BASE_EXPORT DcheckState get_dcheck_state();
-BASE_EXPORT void set_dcheck_state(DcheckState state);
+#if DCHECK_IS_ON
-#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::get_dcheck_state() == \
- ::logging::ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS) && \
- LOG_IS_ON(DCHECK))
-
-#endif // defined(DCHECK_ALWAYS_ON)
-
-#else // defined(NDEBUG)
-
-// 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)
-#else // ENABLE_DCHECK
+#else // DCHECK_IS_ON
-// These are just dummy values since DCHECK_IS_ON() is always false in
-// this case.
+// These are just dummy values.
#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
+#endif // DCHECK_IS_ON
// 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.
-#define DCHECK(condition) \
- LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \
<< "Check failed: " #condition ". "
-#define DPCHECK(condition) \
- LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON() && !(condition)) \
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \
<< "Check failed: " #condition ". "
// 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 (DCHECK_IS_ON) \
if (std::string* _result = \
logging::Check##name##Impl((val1), (val2), \
#val1 " " #op " " #val2)) \
@@ -797,32 +684,14 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
// 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.
+ // Used for LOG(severity).
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
+ // Used for CHECK_EQ(), etc. 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.
+ // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
LogMessage(const char* file, int line, LogSeverity severity,
std::string* result);
@@ -890,6 +759,7 @@ typedef int SystemErrorCode;
// 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();
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code);
#if defined(OS_WIN)
// Appends a formatted system message of the GetLastError() type.
@@ -898,12 +768,6 @@ class BASE_EXPORT Win32ErrorLogMessage {
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.
@@ -913,8 +777,6 @@ class BASE_EXPORT Win32ErrorLogMessage {
private:
SystemErrorCode err_;
- // Optional name of the module defining the error.
- const char* module_;
LogMessage log_message_;
DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc
index 4996abc2240..f823d6ee2d7 100644
--- a/chromium/base/logging_unittest.cc
+++ b/chromium/base/logging_unittest.cc
@@ -17,11 +17,11 @@ using ::testing::Return;
// Needs to be global since log assert handlers can't maintain state.
int log_sink_call_count = 0;
-#if !LOGGING_IS_OFFICIAL_BUILD
+#if !defined(OFFICIAL_BUILD) || defined(DCHECK_ALWAYS_ON) || !defined(NDEBUG)
void LogSink(const std::string& str) {
++log_sink_call_count;
}
-#endif // !LOGGING_IS_OFFICIAL_BUILD
+#endif
// Class to make sure any manipulations we do to the min log level are
// contained (i.e., do not affect other unit tests).
@@ -32,7 +32,6 @@ class LogStateSaver {
~LogStateSaver() {
SetMinLogLevel(old_min_log_level_);
SetLogAssertHandler(NULL);
- SetLogReportHandler(NULL);
log_sink_call_count = 0;
}
@@ -54,11 +53,7 @@ class MockLogSource {
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).
+ EXPECT_CALL(mock_log_source, Log()).Times(DEBUG_MODE ? 16 : 8).
WillRepeatedly(Return("log message"));
SetMinLogLevel(LOG_INFO);
@@ -76,6 +71,8 @@ TEST_F(LoggingTest, BasicLogging) {
PLOG_IF(INFO, true) << mock_log_source.Log();
VLOG(0) << mock_log_source.Log();
VLOG_IF(0, true) << mock_log_source.Log();
+ VPLOG(0) << mock_log_source.Log();
+ VPLOG_IF(0, true) << mock_log_source.Log();
DLOG(INFO) << mock_log_source.Log();
DLOG_IF(INFO, true) << mock_log_source.Log();
@@ -83,6 +80,8 @@ TEST_F(LoggingTest, BasicLogging) {
DPLOG_IF(INFO, true) << mock_log_source.Log();
DVLOG(0) << mock_log_source.Log();
DVLOG_IF(0, true) << mock_log_source.Log();
+ DVPLOG(0) << mock_log_source.Log();
+ DVPLOG_IF(0, true) << mock_log_source.Log();
}
TEST_F(LoggingTest, LogIsOn) {
@@ -96,7 +95,6 @@ TEST_F(LoggingTest, LogIsOn) {
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));
@@ -104,7 +102,6 @@ TEST_F(LoggingTest, LogIsOn) {
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));
@@ -112,33 +109,14 @@ TEST_F(LoggingTest, LogIsOn) {
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_TRUE(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_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL));
-
- // So should LOG_IS_ON(FATAL).
+ // LOG_IS_ON(FATAL) should always be true.
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_TRUE(kDfatalIsFatal == LOG_IS_ON(DFATAL));
}
@@ -159,6 +137,8 @@ TEST_F(LoggingTest, LoggingIsLazy) {
PLOG_IF(INFO, false) << mock_log_source.Log();
VLOG(1) << mock_log_source.Log();
VLOG_IF(1, true) << mock_log_source.Log();
+ VPLOG(1) << mock_log_source.Log();
+ VPLOG_IF(1, true) << mock_log_source.Log();
DLOG(INFO) << mock_log_source.Log();
DLOG_IF(INFO, true) << mock_log_source.Log();
@@ -166,10 +146,12 @@ TEST_F(LoggingTest, LoggingIsLazy) {
DPLOG_IF(INFO, true) << mock_log_source.Log();
DVLOG(1) << mock_log_source.Log();
DVLOG_IF(1, true) << mock_log_source.Log();
+ DVPLOG(1) << mock_log_source.Log();
+ DVPLOG_IF(1, true) << mock_log_source.Log();
}
// Official builds have CHECKs directly call BreakDebugger.
-#if !LOGGING_IS_OFFICIAL_BUILD
+#if !defined(OFFICIAL_BUILD)
TEST_F(LoggingTest, CheckStreamsAreLazy) {
MockLogSource mock_log_source, uncalled_mock_log_source;
@@ -204,9 +186,10 @@ TEST_F(LoggingTest, DebugLoggingReleaseBehavior) {
TEST_F(LoggingTest, DcheckStreamsAreLazy) {
MockLogSource mock_log_source;
EXPECT_CALL(mock_log_source, Log()).Times(0);
-#if !LOGGING_IS_OFFICIAL_BUILD && defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
- // Unofficial release build without dcheck enabled.
- set_dcheck_state(DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+#if DCHECK_IS_ON
+ DCHECK(true) << mock_log_source.Log();
+ DCHECK_EQ(0, 0) << mock_log_source.Log();
+#else
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();
@@ -216,36 +199,29 @@ TEST_F(LoggingTest, DcheckStreamsAreLazy) {
}
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)
- // Unofficial release build.
- set_dcheck_state(ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
- SetLogReportHandler(&LogSink);
- EXPECT_TRUE(DCHECK_IS_ON());
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+ // Release build.
+ EXPECT_FALSE(DCHECK_IS_ON);
EXPECT_FALSE(DLOG_IS_ON(DCHECK));
#elif defined(NDEBUG) && defined(DCHECK_ALWAYS_ON)
- // Unofficial release build with real DCHECKS.
- set_dcheck_state(ENABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
+ // Release build with real DCHECKS.
SetLogAssertHandler(&LogSink);
- EXPECT_TRUE(DCHECK_IS_ON());
+ EXPECT_TRUE(DCHECK_IS_ON);
EXPECT_FALSE(DLOG_IS_ON(DCHECK));
#else
- // Unofficial debug build.
+ // Debug build.
SetLogAssertHandler(&LogSink);
- EXPECT_TRUE(DCHECK_IS_ON());
+ EXPECT_TRUE(DCHECK_IS_ON);
EXPECT_TRUE(DLOG_IS_ON(DCHECK));
-#endif // defined(LOGGING_IS_OFFICIAL_BUILD)
+#endif
EXPECT_EQ(0, log_sink_call_count);
DCHECK(false);
- EXPECT_EQ(DCHECK_IS_ON() ? 1 : 0, log_sink_call_count);
+ EXPECT_EQ(DCHECK_IS_ON ? 1 : 0, log_sink_call_count);
DPCHECK(false);
- EXPECT_EQ(DCHECK_IS_ON() ? 2 : 0, log_sink_call_count);
+ 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);
+ EXPECT_EQ(DCHECK_IS_ON ? 3 : 0, log_sink_call_count);
}
TEST_F(LoggingTest, DcheckReleaseBehavior) {
diff --git a/chromium/base/logging_win.cc b/chromium/base/logging_win.cc
index a7146657884..a3c3a5befe0 100644
--- a/chromium/base/logging_win.cc
+++ b/chromium/base/logging_win.cc
@@ -37,7 +37,6 @@ bool LogEventProvider::LogMessage(logging::LogSeverity severity,
level = TRACE_LEVEL_WARNING;
break;
case LOG_ERROR:
- case LOG_ERROR_REPORT:
level = TRACE_LEVEL_ERROR;
break;
case LOG_FATAL:
diff --git a/chromium/base/mac/bind_objc_block.h b/chromium/base/mac/bind_objc_block.h
index 75da437b5c5..9deb2d22e75 100644
--- a/chromium/base/mac/bind_objc_block.h
+++ b/chromium/base/mac/bind_objc_block.h
@@ -14,7 +14,13 @@
// BindBlock builds a callback from an Objective-C block. Example usages:
//
// Closure closure = BindBlock(^{DoSomething();});
+//
// Callback<int(void)> callback = BindBlock(^{return 42;});
+//
+// Callback<void(const std::string&, const std::string&)> callback =
+// BindBlock(^(const std::string& arg0, const std::string& arg1) {
+// ...
+// });
namespace base {
@@ -33,6 +39,12 @@ R RunBlock(base::mac::ScopedBlock<R(^)(A1)> block, A1 a) {
return extracted_block(a);
}
+template<typename R, typename A1, typename A2>
+R RunBlock(base::mac::ScopedBlock<R(^)(A1, A2)> block, A1 a, A2 b) {
+ R(^extracted_block)(A1, A2) = block.get();
+ return extracted_block(a, b);
+}
+
} // namespace internal
// Construct a callback with no argument from an objective-C block.
@@ -49,6 +61,13 @@ base::Callback<R(A1)> BindBlock(R(^block)(A1)) {
base::mac::ScopedBlock<R(^)(A1)>(Block_copy(block)));
}
+// Construct a callback with two arguments from an objective-C block.
+template<typename R, typename A1, typename A2>
+base::Callback<R(A1, A2)> BindBlock(R(^block)(A1, A2)) {
+ return base::Bind(&base::internal::RunBlock<R, A1, A2>,
+ base::mac::ScopedBlock<R(^)(A1, A2)>(Block_copy(block)));
+}
+
} // namespace base
#endif // BASE_MAC_BIND_OBJC_BLOCK_H_
diff --git a/chromium/base/mac/bind_objc_block_unittest.mm b/chromium/base/mac/bind_objc_block_unittest.mm
index a4bcd76dbb8..c72fd4a8c20 100644
--- a/chromium/base/mac/bind_objc_block_unittest.mm
+++ b/chromium/base/mac/bind_objc_block_unittest.mm
@@ -4,6 +4,8 @@
#import "base/mac/bind_objc_block.h"
+#include <string>
+
#include "base/callback.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -51,4 +53,15 @@ TEST(BindObjcBlockTest, TestArgument) {
EXPECT_EQ(kArgument + 1, c.Run(kArgument));
}
+TEST(BindObjcBlockTest, TestTwoArguments) {
+ std::string result;
+ std::string* ptr = &result;
+ base::Callback<void(const std::string&, const std::string&)> c =
+ base::BindBlock(^(const std::string& a, const std::string& b) {
+ *ptr = a + b;
+ });
+ c.Run("forty", "two");
+ EXPECT_EQ(result, "fortytwo");
+}
+
} // namespace
diff --git a/chromium/base/mac/cocoa_protocols.h b/chromium/base/mac/cocoa_protocols.h
index e83fcbb2996..e10001f6be0 100644
--- a/chromium/base/mac/cocoa_protocols.h
+++ b/chromium/base/mac/cocoa_protocols.h
@@ -7,9 +7,9 @@
#import <Cocoa/Cocoa.h>
-// GTM also maintinas a list of empty protocols, but only the ones the library
+// GTM also maintains a list of empty protocols, but only the ones the library
// requires. Augment that below.
-#import "third_party/GTM/GTMDefines.h"
+#import "third_party/google_toolbox_for_mac/src/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
@@ -30,13 +30,6 @@ DEFINE_EMPTY_PROTOCOL(ICCameraDeviceDownloadDelegate)
#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/chromium/base/mac/foundation_util_unittest.mm b/chromium/base/mac/foundation_util_unittest.mm
index 3b72b1225a8..916a13bd7fa 100644
--- a/chromium/base/mac/foundation_util_unittest.mm
+++ b/chromium/base/mac/foundation_util_unittest.mm
@@ -5,9 +5,12 @@
#include "base/mac/foundation_util.h"
#include "base/basictypes.h"
+#include "base/compiler_specific.h"
#include "base/files/file_path.h"
+#include "base/format_macros.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_nsautorelease_pool.h"
+#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
@@ -314,5 +317,75 @@ TEST(FoundationUtilTest, NSStringToFilePath) {
EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b"));
}
+TEST(StringNumberConversionsTest, FormatNSInteger) {
+ // The PRI[dxu]NS macro assumes that NSInteger is a typedef to "int" on
+ // 32-bit architecture and a typedef to "long" on 64-bit architecture
+ // (respectively "unsigned int" and "unsigned long" for NSUInteger). Use
+ // pointer incompatibility to validate this at compilation.
+#if defined(ARCH_CPU_64_BITS)
+ typedef long FormatNSIntegerAsType;
+ typedef unsigned long FormatNSUIntegerAsType;
+#else
+ typedef int FormatNSIntegerAsType;
+ typedef unsigned int FormatNSUIntegerAsType;
+#endif // defined(ARCH_CPU_64_BITS)
+
+ NSInteger some_nsinteger;
+ FormatNSIntegerAsType* pointer_to_some_nsinteger ALLOW_UNUSED =
+ &some_nsinteger;
+
+ NSUInteger some_nsuinteger;
+ FormatNSUIntegerAsType* pointer_to_some_nsuinteger ALLOW_UNUSED =
+ &some_nsuinteger;
+
+ // Check that format specifier works correctly for NSInteger.
+ const struct {
+ NSInteger value;
+ const char* expected;
+ const char* expected_hex;
+ } nsinteger_cases[] = {
+#if !defined(ARCH_CPU_64_BITS)
+ {12345678, "12345678", "bc614e"},
+ {-12345678, "-12345678", "ff439eb2"},
+#else
+ {12345678, "12345678", "bc614e"},
+ {-12345678, "-12345678", "ffffffffff439eb2"},
+ {137451299150l, "137451299150", "2000bc614e"},
+ {-137451299150l, "-137451299150", "ffffffdfff439eb2"},
+#endif // !defined(ARCH_CPU_64_BITS)
+ };
+
+ for (size_t i = 0; i < arraysize(nsinteger_cases); ++i) {
+ EXPECT_EQ(nsinteger_cases[i].expected,
+ StringPrintf("%" PRIdNS, nsinteger_cases[i].value));
+ EXPECT_EQ(nsinteger_cases[i].expected_hex,
+ StringPrintf("%" PRIxNS, nsinteger_cases[i].value));
+ }
+
+ // Check that format specifier works correctly for NSUInteger.
+ const struct {
+ NSUInteger value;
+ const char* expected;
+ const char* expected_hex;
+ } nsuinteger_cases[] = {
+#if !defined(ARCH_CPU_64_BITS)
+ {12345678u, "12345678", "bc614e"},
+ {4282621618u, "4282621618", "ff439eb2"},
+#else
+ {12345678u, "12345678", "bc614e"},
+ {4282621618u, "4282621618", "ff439eb2"},
+ {137451299150ul, "137451299150", "2000bc614e"},
+ {18446743936258252466ul, "18446743936258252466", "ffffffdfff439eb2"},
+#endif // !defined(ARCH_CPU_64_BITS)
+ };
+
+ for (size_t i = 0; i < arraysize(nsuinteger_cases); ++i) {
+ EXPECT_EQ(nsuinteger_cases[i].expected,
+ StringPrintf("%" PRIuNS, nsuinteger_cases[i].value));
+ EXPECT_EQ(nsuinteger_cases[i].expected_hex,
+ StringPrintf("%" PRIxNS, nsuinteger_cases[i].value));
+ }
+}
+
} // namespace mac
} // namespace base
diff --git a/chromium/base/mac/launch_services_util.h b/chromium/base/mac/launch_services_util.h
index d4aa9ffcbeb..0c52ca94c52 100644
--- a/chromium/base/mac/launch_services_util.h
+++ b/chromium/base/mac/launch_services_util.h
@@ -22,7 +22,7 @@ namespace mac {
// |out_psn|, if not NULL, will be set to the process serial number of the
// application's main process if the app was successfully launched.
// Returns true if the app was successfully launched.
-BASE_EXPORT bool OpenApplicationWithPath(const base::FilePath& bundle_path,
+BASE_EXPORT bool OpenApplicationWithPath(const FilePath& bundle_path,
const CommandLine& command_line,
LSLaunchFlags launch_flags,
ProcessSerialNumber* out_psn);
diff --git a/chromium/base/mac/mac_logging.h b/chromium/base/mac/mac_logging.h
index 9a0003e9ad8..1081490ec4a 100644
--- a/chromium/base/mac/mac_logging.h
+++ b/chromium/base/mac/mac_logging.h
@@ -5,6 +5,8 @@
#ifndef BASE_MAC_MAC_LOGGING_H_
#define BASE_MAC_MAC_LOGGING_H_
+#include "base/base_export.h"
+#include "base/basictypes.h"
#include "base/logging.h"
#include "build/build_config.h"
@@ -43,6 +45,12 @@ class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage {
} // namespace logging
+#if defined(NDEBUG)
+#define MAC_DVLOG_IS_ON(verbose_level) 0
+#else
+#define MAC_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level)
+#endif
+
#define OSSTATUS_LOG_STREAM(severity, status) \
COMPACT_GOOGLE_LOG_EX_ ## severity(OSStatusLogMessage, status).stream()
#define OSSTATUS_VLOG_STREAM(verbose_level, status) \
@@ -73,15 +81,15 @@ class BASE_EXPORT OSStatusLogMessage : public logging::LogMessage {
DLOG_IS_ON(severity) && (condition))
#define OSSTATUS_DVLOG(verbose_level, status) \
- LAZY_STREAM(OSSTATUS_VPLOG_STREAM(verbose_level, status), \
- DVLOG_IS_ON(verbose_level))
+ LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \
+ MAC_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))
+ LAZY_STREAM(OSSTATUS_VLOG_STREAM(verbose_level, status), \
+ MAC_DVLOG_IS_ON(verbose_level) && (condition))
#define OSSTATUS_DCHECK(condition, status) \
LAZY_STREAM(OSSTATUS_LOG_STREAM(FATAL, status), \
- DCHECK_IS_ON() && !(condition)) \
+ DCHECK_IS_ON && !(condition)) \
<< "Check failed: " # condition << ". "
#endif // BASE_MAC_MAC_LOGGING_H_
diff --git a/chromium/base/mac/mac_util.h b/chromium/base/mac/mac_util.h
index e827f37c9fb..db399a8ca06 100644
--- a/chromium/base/mac/mac_util.h
+++ b/chromium/base/mac/mac_util.h
@@ -113,6 +113,12 @@ BASE_EXPORT void RemoveFromLoginItems();
BASE_EXPORT bool WasLaunchedAsLoginOrResumeItem();
// Returns true if the current process was automatically launched as a
+// 'Login Item' or via Resume, and the 'Reopen windows when logging back in'
+// checkbox was selected by the user. This indicates that the previous
+// session should be restored.
+BASE_EXPORT bool WasLaunchedAsLoginItemRestoreState();
+
+// 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();
@@ -140,17 +146,23 @@ BASE_EXPORT bool IsOSMountainLionOrLater();
// Mavericks is Mac OS X 10.9, Darwin 13.
BASE_EXPORT bool IsOSMavericks();
+BASE_EXPORT bool IsOSMavericksOrEarlier();
BASE_EXPORT bool IsOSMavericksOrLater();
+// Yosemite is Mac OS X 10.10, Darwin 14.
+BASE_EXPORT bool IsOSYosemite();
+BASE_EXPORT bool IsOSYosemiteOrLater();
+
// 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 IsOSLaterThanMavericks_DontCallThis();
+BASE_EXPORT bool IsOSLaterThanYosemite_DontCallThis();
// Inline functions that are redundant due to version ranges being mutually-
// exclusive.
inline bool IsOSLionOrEarlier() { return !IsOSMountainLionOrLater(); }
inline bool IsOSMountainLionOrEarlier() { return !IsOSMavericksOrLater(); }
+inline bool IsOSMavericksOrEarlier() { return !IsOSYosemiteOrLater(); }
// 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
@@ -192,7 +204,19 @@ inline bool IsOSMavericksOrLater() { return true; }
MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9
inline bool IsOSMavericks() { return false; }
-inline bool IsOSLaterThanMavericks_DontCallThis() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_10) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_10
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_10
+inline bool IsOSYosemiteOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_10) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_10
+#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10
+inline bool IsOSYosemite() { return false; }
+inline bool IsOSLaterThanYosemite_DontCallThis() { return true; }
#endif
// Retrieve the system's model identifier string from the IOKit registry:
diff --git a/chromium/base/mac/mac_util.mm b/chromium/base/mac/mac_util.mm
index c2565991abb..4ff1c879237 100644
--- a/chromium/base/mac/mac_util.mm
+++ b/chromium/base/mac/mac_util.mm
@@ -20,6 +20,7 @@
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_ioobject.h"
#include "base/mac/scoped_nsobject.h"
+#include "base/mac/sdk_forward_declarations.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/sys_string_conversions.h"
@@ -27,16 +28,6 @@
namespace base {
namespace mac {
-// Replicate specific 10.7 SDK declarations for building with prior SDKs.
-#if !defined(MAC_OS_X_VERSION_10_7) || \
- MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
-
-enum {
- NSApplicationPresentationFullScreen = 1 << 10
-};
-
-#endif // MAC_OS_X_VERSION_10_7
-
namespace {
// The current count of outstanding requests for full screen mode from browser
@@ -385,6 +376,30 @@ bool WasLaunchedAsLoginOrResumeItem() {
return result == YES;
}
+bool WasLaunchedAsLoginItemRestoreState() {
+ // "Reopen windows..." option was added for Lion. Prior OS versions should
+ // not have this behavior.
+ if (IsOSSnowLeopard() || !WasLaunchedAsLoginOrResumeItem())
+ return false;
+
+ CFStringRef app = CFSTR("com.apple.loginwindow");
+ CFStringRef save_state = CFSTR("TALLogoutSavesState");
+ ScopedCFTypeRef<CFPropertyListRef> plist(
+ CFPreferencesCopyAppValue(save_state, app));
+ // According to documentation, com.apple.loginwindow.plist does not exist on a
+ // fresh installation until the user changes a login window setting. The
+ // "reopen windows" option is checked by default, so the plist would exist had
+ // the user unchecked it.
+ // https://developer.apple.com/library/mac/documentation/macosx/conceptual/bpsystemstartup/chapters/CustomLogin.html
+ if (!plist)
+ return true;
+
+ if (CFBooleanRef restore_state = base::mac::CFCast<CFBooleanRef>(plist))
+ return CFBooleanGetValue(restore_state);
+
+ return false;
+}
+
bool WasLaunchedAsHiddenLoginItem() {
if (!WasLaunchedAsLoginOrResumeItem())
return false;
@@ -469,7 +484,7 @@ int MacOSXMinorVersionInternal() {
// immediate death.
CHECK(darwin_major_version >= 6);
int mac_os_x_minor_version = darwin_major_version - 4;
- DLOG_IF(WARNING, darwin_major_version > 13) << "Assuming Darwin "
+ DLOG_IF(WARNING, darwin_major_version > 14) << "Assuming Darwin "
<< base::IntToString(darwin_major_version) << " is Mac OS X 10."
<< base::IntToString(mac_os_x_minor_version);
@@ -488,6 +503,7 @@ enum {
LION_MINOR_VERSION = 7,
MOUNTAIN_LION_MINOR_VERSION = 8,
MAVERICKS_MINOR_VERSION = 9,
+ YOSEMITE_MINOR_VERSION = 10,
};
} // namespace
@@ -534,9 +550,21 @@ bool IsOSMavericksOrLater() {
}
#endif
-#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9)
-bool IsOSLaterThanMavericks_DontCallThis() {
- return MacOSXMinorVersion() > MAVERICKS_MINOR_VERSION;
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10)
+bool IsOSYosemite() {
+ return MacOSXMinorVersion() == YOSEMITE_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_10)
+bool IsOSYosemiteOrLater() {
+ return MacOSXMinorVersion() >= YOSEMITE_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_10)
+bool IsOSLaterThanYosemite_DontCallThis() {
+ return MacOSXMinorVersion() > YOSEMITE_MINOR_VERSION;
}
#endif
diff --git a/chromium/base/mac/mac_util_unittest.mm b/chromium/base/mac/mac_util_unittest.mm
index 1b56814abec..956036eaf1b 100644
--- a/chromium/base/mac/mac_util_unittest.mm
+++ b/chromium/base/mac/mac_util_unittest.mm
@@ -108,7 +108,7 @@ TEST_F(MacUtilTest, TestExcludeFileFromBackups) {
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)));
+ WriteFile(dummy_file_path, dummy_data, arraysize(dummy_data)));
NSString* fileURLString =
[NSString stringWithUTF8String:dummy_file_path.value().c_str()];
NSURL* fileURL = [NSURL URLWithString:fileURLString];
@@ -150,8 +150,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) {
EXPECT_TRUE(IsOSMountainLionOrEarlier());
EXPECT_FALSE(IsOSMountainLionOrLater());
EXPECT_FALSE(IsOSMavericks());
+ EXPECT_TRUE(IsOSMavericksOrEarlier());
EXPECT_FALSE(IsOSMavericksOrLater());
- EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
+ EXPECT_FALSE(IsOSYosemite());
+ EXPECT_FALSE(IsOSYosemiteOrLater());
+ EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
} else if (minor == 7) {
EXPECT_FALSE(IsOSSnowLeopard());
EXPECT_TRUE(IsOSLion());
@@ -161,8 +164,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) {
EXPECT_TRUE(IsOSMountainLionOrEarlier());
EXPECT_FALSE(IsOSMountainLionOrLater());
EXPECT_FALSE(IsOSMavericks());
+ EXPECT_TRUE(IsOSMavericksOrEarlier());
EXPECT_FALSE(IsOSMavericksOrLater());
- EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
+ EXPECT_FALSE(IsOSYosemite());
+ EXPECT_FALSE(IsOSYosemiteOrLater());
+ EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
} else if (minor == 8) {
EXPECT_FALSE(IsOSSnowLeopard());
EXPECT_FALSE(IsOSLion());
@@ -172,8 +178,11 @@ TEST_F(MacUtilTest, IsOSEllipsis) {
EXPECT_TRUE(IsOSMountainLionOrEarlier());
EXPECT_TRUE(IsOSMountainLionOrLater());
EXPECT_FALSE(IsOSMavericks());
+ EXPECT_TRUE(IsOSMavericksOrEarlier());
EXPECT_FALSE(IsOSMavericksOrLater());
- EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
+ EXPECT_FALSE(IsOSYosemite());
+ EXPECT_FALSE(IsOSYosemiteOrLater());
+ EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
} else if (minor == 9) {
EXPECT_FALSE(IsOSSnowLeopard());
EXPECT_FALSE(IsOSLion());
@@ -183,10 +192,27 @@ TEST_F(MacUtilTest, IsOSEllipsis) {
EXPECT_FALSE(IsOSMountainLionOrEarlier());
EXPECT_TRUE(IsOSMountainLionOrLater());
EXPECT_TRUE(IsOSMavericks());
+ EXPECT_TRUE(IsOSMavericksOrEarlier());
EXPECT_TRUE(IsOSMavericksOrLater());
- EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
+ EXPECT_FALSE(IsOSYosemite());
+ EXPECT_FALSE(IsOSYosemiteOrLater());
+ EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
+ } else if (minor == 10) {
+ EXPECT_FALSE(IsOSSnowLeopard());
+ EXPECT_FALSE(IsOSLion());
+ EXPECT_FALSE(IsOSLionOrEarlier());
+ EXPECT_TRUE(IsOSLionOrLater());
+ EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_FALSE(IsOSMountainLionOrEarlier());
+ EXPECT_TRUE(IsOSMountainLionOrLater());
+ EXPECT_FALSE(IsOSMavericks());
+ EXPECT_FALSE(IsOSMavericksOrEarlier());
+ EXPECT_TRUE(IsOSMavericksOrLater());
+ EXPECT_TRUE(IsOSYosemite());
+ EXPECT_TRUE(IsOSYosemiteOrLater());
+ EXPECT_FALSE(IsOSLaterThanYosemite_DontCallThis());
} else {
- // Not five, six, seven, eight, or nine. Ah, ah, ah.
+ // Not six, seven, eight, nine, or ten. Ah, ah, ah.
EXPECT_TRUE(false);
}
} else {
diff --git a/chromium/base/mac/mach_logging.cc b/chromium/base/mac/mach_logging.cc
new file mode 100644
index 00000000000..c5ff85e662f
--- /dev/null
+++ b/chromium/base/mac/mach_logging.cc
@@ -0,0 +1,87 @@
+// Copyright 2014 The Chromium Authors. All 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/mach_logging.h"
+
+#include <iomanip>
+#include <string>
+
+#include "base/strings/stringprintf.h"
+
+#if !defined(OS_IOS)
+#include <servers/bootstrap.h>
+#endif // !OS_IOS
+
+namespace {
+
+std::string FormatMachErrorNumber(mach_error_t mach_err) {
+ // For the os/kern subsystem, give the error number in decimal as in
+ // <mach/kern_return.h>. Otherwise, give it in hexadecimal to make it easier
+ // to visualize the various bits. See <mach/error.h>.
+ if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) {
+ return base::StringPrintf(" (%d)", mach_err);
+ }
+ return base::StringPrintf(" (0x%08x)", mach_err);
+}
+
+} // namespace
+
+namespace logging {
+
+MachLogMessage::MachLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ mach_error_t mach_err)
+ : LogMessage(file_path, line, severity),
+ mach_err_(mach_err) {
+}
+
+MachLogMessage::~MachLogMessage() {
+ stream() << ": "
+ << mach_error_string(mach_err_)
+ << FormatMachErrorNumber(mach_err_);
+}
+
+#if !defined(OS_IOS)
+
+BootstrapLogMessage::BootstrapLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ kern_return_t bootstrap_err)
+ : LogMessage(file_path, line, severity),
+ bootstrap_err_(bootstrap_err) {
+}
+
+BootstrapLogMessage::~BootstrapLogMessage() {
+ stream() << ": "
+ << bootstrap_strerror(bootstrap_err_);
+
+ switch (bootstrap_err_) {
+ case BOOTSTRAP_SUCCESS:
+ case BOOTSTRAP_NOT_PRIVILEGED:
+ case BOOTSTRAP_NAME_IN_USE:
+ case BOOTSTRAP_UNKNOWN_SERVICE:
+ case BOOTSTRAP_SERVICE_ACTIVE:
+ case BOOTSTRAP_BAD_COUNT:
+ case BOOTSTRAP_NO_MEMORY:
+ case BOOTSTRAP_NO_CHILDREN: {
+ // Show known bootstrap errors in decimal because that's how they're
+ // defined in <servers/bootstrap.h>.
+ stream() << " (" << bootstrap_err_ << ")";
+ break;
+ }
+
+ default: {
+ // bootstrap_strerror passes unknown errors to mach_error_string, so
+ // format them as they would be if they were handled by
+ // MachErrorMessage.
+ stream() << FormatMachErrorNumber(bootstrap_err_);
+ break;
+ }
+ }
+}
+
+#endif // !OS_IOS
+
+} // namespace logging
diff --git a/chromium/base/mac/mach_logging.h b/chromium/base/mac/mach_logging.h
new file mode 100644
index 00000000000..a9b3b65d648
--- /dev/null
+++ b/chromium/base/mac/mach_logging.h
@@ -0,0 +1,167 @@
+// Copyright 2014 The Chromium Authors. All 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_MACH_LOGGING_H_
+#define BASE_MAC_MACH_LOGGING_H_
+
+#include <mach/mach.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "build/build_config.h"
+
+// Use the MACH_LOG family of macros along with a mach_error_t (kern_return_t)
+// containing a Mach error. The error value will be decoded so that logged
+// messages explain the error.
+//
+// Use the BOOTSTRAP_LOG family of macros specifically for errors that occur
+// while interoperating with the bootstrap subsystem. These errors will first
+// be looked up as bootstrap error messages. If no match is found, they will
+// be treated as generic Mach errors, as in MACH_LOG.
+//
+// Examples:
+//
+// kern_return_t kr = mach_timebase_info(&info);
+// if (kr != KERN_SUCCESS) {
+// MACH_LOG(ERROR, kr) << "mach_timebase_info";
+// }
+//
+// kr = vm_deallocate(task, address, size);
+// MACH_DCHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate";
+
+namespace logging {
+
+class BASE_EXPORT MachLogMessage : public logging::LogMessage {
+ public:
+ MachLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ mach_error_t mach_err);
+ ~MachLogMessage();
+
+ private:
+ mach_error_t mach_err_;
+
+ DISALLOW_COPY_AND_ASSIGN(MachLogMessage);
+};
+
+} // namespace logging
+
+#if defined(NDEBUG)
+#define MACH_DVLOG_IS_ON(verbose_level) 0
+#else
+#define MACH_DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level)
+#endif
+
+#define MACH_LOG_STREAM(severity, mach_err) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(MachLogMessage, mach_err).stream()
+#define MACH_VLOG_STREAM(verbose_level, mach_err) \
+ logging::MachLogMessage(__FILE__, __LINE__, \
+ -verbose_level, mach_err).stream()
+
+#define MACH_LOG(severity, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), LOG_IS_ON(severity))
+#define MACH_LOG_IF(severity, condition, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \
+ LOG_IS_ON(severity) && (condition))
+
+#define MACH_VLOG(verbose_level, mach_err) \
+ LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \
+ VLOG_IS_ON(verbose_level))
+#define MACH_VLOG_IF(verbose_level, condition, mach_err) \
+ LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+#define MACH_CHECK(condition, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#define MACH_DLOG(severity, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), DLOG_IS_ON(severity))
+#define MACH_DLOG_IF(severity, condition, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \
+ DLOG_IS_ON(severity) && (condition))
+
+#define MACH_DVLOG(verbose_level, mach_err) \
+ LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \
+ MACH_DVLOG_IS_ON(verbose_level))
+#define MACH_DVLOG_IF(verbose_level, condition, mach_err) \
+ LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \
+ MACH_DVLOG_IS_ON(verbose_level) && (condition))
+
+#define MACH_DCHECK(condition, mach_err) \
+ LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), \
+ DCHECK_IS_ON && !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#if !defined(OS_IOS)
+
+namespace logging {
+
+class BASE_EXPORT BootstrapLogMessage : public logging::LogMessage {
+ public:
+ BootstrapLogMessage(const char* file_path,
+ int line,
+ LogSeverity severity,
+ kern_return_t bootstrap_err);
+ ~BootstrapLogMessage();
+
+ private:
+ kern_return_t bootstrap_err_;
+
+ DISALLOW_COPY_AND_ASSIGN(BootstrapLogMessage);
+};
+
+} // namespace logging
+
+#define BOOTSTRAP_DVLOG_IS_ON MACH_DVLOG_IS_ON
+
+#define BOOTSTRAP_LOG_STREAM(severity, bootstrap_err) \
+ COMPACT_GOOGLE_LOG_EX_ ## severity(BootstrapLogMessage, \
+ bootstrap_err).stream()
+#define BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err) \
+ logging::BootstrapLogMessage(__FILE__, __LINE__, \
+ -verbose_level, bootstrap_err).stream()
+
+#define BOOTSTRAP_LOG(severity, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, \
+ bootstrap_err), LOG_IS_ON(severity))
+#define BOOTSTRAP_LOG_IF(severity, condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \
+ LOG_IS_ON(severity) && (condition))
+
+#define BOOTSTRAP_VLOG(verbose_level, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \
+ VLOG_IS_ON(verbose_level))
+#define BOOTSTRAP_VLOG_IF(verbose_level, condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+#define BOOTSTRAP_CHECK(condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#define BOOTSTRAP_DLOG(severity, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \
+ DLOG_IS_ON(severity))
+#define BOOTSTRAP_DLOG_IF(severity, condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \
+ DLOG_IS_ON(severity) && (condition))
+
+#define BOOTSTRAP_DVLOG(verbose_level, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \
+ BOOTSTRAP_DVLOG_IS_ON(verbose_level))
+#define BOOTSTRAP_DVLOG_IF(verbose_level, condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \
+ BOOTSTRAP_DVLOG_IS_ON(verbose_level) && (condition))
+
+#define BOOTSTRAP_DCHECK(condition, bootstrap_err) \
+ LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), \
+ DCHECK_IS_ON && !(condition)) \
+ << "Check failed: " # condition << ". "
+
+#endif // !OS_IOS
+
+#endif // BASE_MAC_MACH_LOGGING_H_
diff --git a/chromium/base/mac/os_crash_dumps.cc b/chromium/base/mac/os_crash_dumps.cc
index e6b0996ac6d..5d65b469ddd 100644
--- a/chromium/base/mac/os_crash_dumps.cc
+++ b/chromium/base/mac/os_crash_dumps.cc
@@ -30,11 +30,14 @@ void DisableOSCrashDumps() {
// bsd/uxkern/ux_exception.c and machine_exception() in xnu's
// bsd/dev/*/unix_signal.c.
const int signals_to_intercept[] = {
+ // Hardware faults
SIGILL, // EXC_BAD_INSTRUCTION
SIGTRAP, // EXC_BREAKPOINT
SIGFPE, // EXC_ARITHMETIC
SIGBUS, // EXC_BAD_ACCESS
- SIGSEGV // EXC_BAD_ACCESS
+ SIGSEGV, // EXC_BAD_ACCESS
+ // Not a hardware fault
+ SIGABRT
};
// For all these signals, just wire things up so we exit immediately.
@@ -47,9 +50,9 @@ void DisableOSCrashDumps() {
act.sa_flags = SA_ONSTACK;
if (sigemptyset(&act.sa_mask) != 0)
- DLOG_ERRNO(FATAL) << "sigemptyset() failed";
+ DPLOG(FATAL) << "sigemptyset() failed";
if (sigaction(signals_to_intercept[i], &act, NULL) != 0)
- DLOG_ERRNO(FATAL) << "sigaction() failed";
+ DPLOG(FATAL) << "sigaction() failed";
}
}
diff --git a/chromium/base/mac/scoped_cftyperef.h b/chromium/base/mac/scoped_cftyperef.h
index c41de80d805..8567f85ffcd 100644
--- a/chromium/base/mac/scoped_cftyperef.h
+++ b/chromium/base/mac/scoped_cftyperef.h
@@ -7,9 +7,7 @@
#include <CoreFoundation/CoreFoundation.h>
-#include "base/basictypes.h"
-#include "base/compiler_specific.h"
-#include "base/memory/scoped_policy.h"
+#include "base/mac/scoped_typeref.h"
namespace base {
@@ -27,78 +25,33 @@ namespace base {
// then ScopedCFTypeRef<> will call CFRetain() on the object, and the initial
// ownership is not changed.
+namespace internal {
+
+struct ScopedCFTypeRefTraits {
+ static void Retain(CFTypeRef object) {
+ CFRetain(object);
+ }
+ static void Release(CFTypeRef object) {
+ CFRelease(object);
+ }
+};
+
+} // namespace internal
+
template<typename CFT>
-class ScopedCFTypeRef {
+class ScopedCFTypeRef
+ : public ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits> {
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_);
- }
+ : ScopedTypeRef<CFT,
+ internal::ScopedCFTypeRefTraits>(object, policy) {}
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_;
+ : ScopedTypeRef<CFT, internal::ScopedCFTypeRefTraits>(that) {}
};
} // namespace base
diff --git a/chromium/base/mac/scoped_mach_port.cc b/chromium/base/mac/scoped_mach_port.cc
index 9e45a856a8c..de94602e36e 100644
--- a/chromium/base/mac/scoped_mach_port.cc
+++ b/chromium/base/mac/scoped_mach_port.cc
@@ -4,22 +4,27 @@
#include "base/mac/scoped_mach_port.h"
+#include "base/mac/mach_logging.h"
+
namespace base {
namespace mac {
+namespace internal {
-ScopedMachPort::ScopedMachPort(mach_port_t port) : port_(port) {
-}
-
-ScopedMachPort::~ScopedMachPort() {
- reset();
+// static
+void SendRightTraits::Free(mach_port_t port) {
+ kern_return_t kr = mach_port_deallocate(mach_task_self(), port);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "ScopedMachSendRight mach_port_deallocate";
}
-void ScopedMachPort::reset(mach_port_t port) {
- if (port_ != MACH_PORT_NULL) {
- mach_port_deallocate(mach_task_self(), port_);
- }
- port_ = port;
+// static
+void ReceiveRightTraits::Free(mach_port_t port) {
+ kern_return_t kr =
+ mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
+ MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
+ << "ScopedMachReceiveRight mach_port_mod_refs";
}
+} // namespace internal
} // namespace mac
} // namespace base
diff --git a/chromium/base/mac/scoped_mach_port.h b/chromium/base/mac/scoped_mach_port.h
index cc2ef20fe76..36087c9bdec 100644
--- a/chromium/base/mac/scoped_mach_port.h
+++ b/chromium/base/mac/scoped_mach_port.h
@@ -7,35 +7,56 @@
#include <mach/mach.h>
-#include "base/basictypes.h"
#include "base/base_export.h"
+#include "base/scoped_generic.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);
+namespace internal {
- ~ScopedMachPort();
+struct BASE_EXPORT SendRightTraits {
+ static mach_port_t InvalidValue() {
+ return MACH_PORT_NULL;
+ }
- void reset(mach_port_t port = MACH_PORT_NULL);
+ static void Free(mach_port_t port);
+};
- operator mach_port_t() const {
- return port_;
+struct BASE_EXPORT ReceiveRightTraits {
+ static mach_port_t InvalidValue() {
+ return MACH_PORT_NULL;
}
- mach_port_t get() const {
- return port_;
- }
+ static void Free(mach_port_t port);
+};
+
+} // namespace internal
- private:
- mach_port_t port_;
+// A scoper for handling a Mach port that names a send right. Send rights are
+// reference counted, and this takes ownership of the right on construction
+// and then removes a reference to the right on destruction. If the reference
+// is the last one on the right, the right is deallocated.
+class BASE_EXPORT ScopedMachSendRight :
+ public base::ScopedGeneric<mach_port_t, internal::SendRightTraits> {
+ public:
+ explicit ScopedMachSendRight(mach_port_t port = traits_type::InvalidValue())
+ : ScopedGeneric(port) {}
+
+ operator mach_port_t() const { return get(); }
+};
+
+// A scoper for handling a Mach port's receive right. There is only one
+// receive right per port. This takes ownership of the receive right on
+// construction and then destroys the right on destruction, turning all
+// outstanding send rights into dead names.
+class BASE_EXPORT ScopedMachReceiveRight :
+ public base::ScopedGeneric<mach_port_t, internal::ReceiveRightTraits> {
+ public:
+ explicit ScopedMachReceiveRight(
+ mach_port_t port = traits_type::InvalidValue()) : ScopedGeneric(port) {}
- DISALLOW_COPY_AND_ASSIGN(ScopedMachPort);
+ operator mach_port_t() const { return get(); }
};
} // namespace mac
diff --git a/chromium/base/mac/scoped_mach_vm.cc b/chromium/base/mac/scoped_mach_vm.cc
new file mode 100644
index 00000000000..1c28d9c7c42
--- /dev/null
+++ b/chromium/base/mac/scoped_mach_vm.cc
@@ -0,0 +1,33 @@
+// Copyright 2014 The Chromium Authors. All 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_vm.h"
+
+namespace base {
+namespace mac {
+
+void ScopedMachVM::reset(vm_address_t address, vm_size_t size) {
+ DCHECK(address % PAGE_SIZE == 0);
+ DCHECK(size % PAGE_SIZE == 0);
+
+ if (size_) {
+ if (address_ < address) {
+ vm_deallocate(mach_task_self(),
+ address_,
+ std::min(size_, address - address_));
+ }
+ if (address_ + size_ > address + size) {
+ vm_address_t deallocate_start = std::max(address_, address + size);
+ vm_deallocate(mach_task_self(),
+ deallocate_start,
+ address_ + size_ - deallocate_start);
+ }
+ }
+
+ address_ = address;
+ size_ = size;
+}
+
+} // namespace mac
+} // namespace base
diff --git a/chromium/base/mac/scoped_mach_vm.h b/chromium/base/mac/scoped_mach_vm.h
new file mode 100644
index 00000000000..b130a79fb63
--- /dev/null
+++ b/chromium/base/mac/scoped_mach_vm.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All 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_VM_H_
+#define BASE_MAC_SCOPED_MACH_VM_H_
+
+#include <mach/mach.h>
+
+#include <algorithm>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// Use ScopedMachVM to supervise ownership of pages in the current process
+// through the Mach VM subsystem. Pages allocated with vm_allocate can be
+// released when exiting a scope with ScopedMachVM.
+//
+// The Mach VM subsystem operates on a page-by-page basis, and a single VM
+// allocation managed by a ScopedMachVM object may span multiple pages. As far
+// as Mach is concerned, allocated pages may be deallocated individually. This
+// is in contrast to higher-level allocators such as malloc, where the base
+// address of an allocation implies the size of an allocated block.
+// Consequently, it is not sufficient to just pass the base address of an
+// allocation to ScopedMachVM, it also needs to know the size of the
+// allocation. To avoid any confusion, both the base address and size must
+// be page-aligned.
+//
+// When dealing with Mach VM, base addresses will naturally be page-aligned,
+// but user-specified sizes may not be. If there's a concern that a size is
+// not page-aligned, use the mach_vm_round_page macro to correct it.
+//
+// Example:
+//
+// vm_address_t address = 0;
+// vm_size_t size = 12345; // This requested size is not page-aligned.
+// kern_return_t kr =
+// vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE);
+// if (kr != KERN_SUCCESS) {
+// return false;
+// }
+// ScopedMachVM vm_owner(address, mach_vm_round_page(size));
+
+namespace base {
+namespace mac {
+
+class BASE_EXPORT ScopedMachVM {
+ public:
+ explicit ScopedMachVM(vm_address_t address = 0, vm_size_t size = 0)
+ : address_(address),
+ size_(size) {
+ DCHECK(address % PAGE_SIZE == 0);
+ DCHECK(size % PAGE_SIZE == 0);
+ }
+
+ ~ScopedMachVM() {
+ if (size_) {
+ vm_deallocate(mach_task_self(), address_, size_);
+ }
+ }
+
+ void reset(vm_address_t address = 0, vm_size_t size = 0);
+
+ vm_address_t address() const {
+ return address_;
+ }
+
+ vm_size_t size() const {
+ return size_;
+ }
+
+ void swap(ScopedMachVM& that) {
+ std::swap(address_, that.address_);
+ std::swap(size_, that.size_);
+ }
+
+ void release() {
+ address_ = 0;
+ size_ = 0;
+ }
+
+ private:
+ vm_address_t address_;
+ vm_size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedMachVM);
+};
+
+} // namespace mac
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_MACH_VM_H_
diff --git a/chromium/base/mac/scoped_typeref.h b/chromium/base/mac/scoped_typeref.h
new file mode 100644
index 00000000000..61ee3119817
--- /dev/null
+++ b/chromium/base/mac/scoped_typeref.h
@@ -0,0 +1,132 @@
+// Copyright 2014 The Chromium Authors. All 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_TYPEREF_H_
+#define BASE_MAC_SCOPED_TYPEREF_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_policy.h"
+
+namespace base {
+
+// ScopedTypeRef<> is patterned after scoped_ptr<>, but maintains a ownership
+// of a reference to any type that is maintained by Retain and Release methods.
+//
+// The Traits structure must provide the Retain and Release methods for type T.
+// A default ScopedTypeRefTraits is used but not defined, and should be defined
+// for each type to use this interface. For example, an appropriate definition
+// of ScopedTypeRefTraits for CGLContextObj would be:
+//
+// template<>
+// struct ScopedTypeRefTraits<CGLContextObj> {
+// void Retain(CGLContextObj object) { CGLContextRetain(object); }
+// void Release(CGLContextObj object) { CGLContextRelease(object); }
+// };
+//
+// For the many types that have pass-by-pointer create functions, the function
+// InitializeInto() is provided to allow direct initialization and assumption
+// of ownership of the object. For example, continuing to use the above
+// CGLContextObj specialization:
+//
+// base::ScopedTypeRef<CGLContextObj> context;
+// CGLCreateContext(pixel_format, share_group, context.InitializeInto());
+//
+// For initialization with an existing object, the caller may specify whether
+// the ScopedTypeRef<> being initialized is assuming the caller's existing
+// ownership of the object (and should not call Retain in initialization) or if
+// it should not assume this ownership and must create its own (by calling
+// Retain in initialization). This behavior is based on the |policy| parameter,
+// with |ASSUME| for the former and |RETAIN| for the latter. The default policy
+// is to |ASSUME|.
+
+template<typename T>
+struct ScopedTypeRefTraits;
+
+template<typename T, typename Traits = ScopedTypeRefTraits<T>>
+class ScopedTypeRef {
+ public:
+ typedef T element_type;
+
+ ScopedTypeRef(
+ T object = NULL,
+ base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME)
+ : object_(object) {
+ if (object_ && policy == base::scoped_policy::RETAIN)
+ Traits::Retain(object_);
+ }
+
+ ScopedTypeRef(const ScopedTypeRef<T, Traits>& that)
+ : object_(that.object_) {
+ if (object_)
+ Traits::Retain(object_);
+ }
+
+ ~ScopedTypeRef() {
+ if (object_)
+ Traits::Release(object_);
+ }
+
+ ScopedTypeRef& operator=(const ScopedTypeRef<T, Traits>& that) {
+ reset(that.get(), base::scoped_policy::RETAIN);
+ return *this;
+ }
+
+ // This is to be used only to take ownership of objects that are created
+ // by pass-by-pointer create functions. To enforce this, require that the
+ // object be reset to NULL before this may be used.
+ T* InitializeInto() WARN_UNUSED_RESULT {
+ DCHECK(!object_);
+ return &object_;
+ }
+
+ void reset(T object = NULL,
+ base::scoped_policy::OwnershipPolicy policy =
+ base::scoped_policy::ASSUME) {
+ if (object && policy == base::scoped_policy::RETAIN)
+ Traits::Retain(object);
+ if (object_)
+ Traits::Release(object_);
+ object_ = object;
+ }
+
+ bool operator==(T that) const {
+ return object_ == that;
+ }
+
+ bool operator!=(T that) const {
+ return object_ != that;
+ }
+
+ operator T() const {
+ return object_;
+ }
+
+ T get() const {
+ return object_;
+ }
+
+ void swap(ScopedTypeRef& that) {
+ T temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ // ScopedTypeRef<>::release() is like scoped_ptr<>::release. It is NOT
+ // a wrapper for Release(). To force a ScopedTypeRef<> object to call
+ // Release(), use ScopedTypeRef<>::reset().
+ T release() WARN_UNUSED_RESULT {
+ T temp = object_;
+ object_ = NULL;
+ return temp;
+ }
+
+ private:
+ T object_;
+};
+
+} // namespace base
+
+#endif // BASE_MAC_SCOPED_TYPEREF_H_
diff --git a/chromium/base/mac/sdk_forward_declarations.h b/chromium/base/mac/sdk_forward_declarations.h
index bbaf962b639..3a0878daef6 100644
--- a/chromium/base/mac/sdk_forward_declarations.h
+++ b/chromium/base/mac/sdk_forward_declarations.h
@@ -12,21 +12,39 @@
#define BASE_MAC_SDK_FORWARD_DECLARATIONS_H_
#import <AppKit/AppKit.h>
+#import <CoreWLAN/CoreWLAN.h>
+#import <ImageCaptureCore/ImageCaptureCore.h>
+#import <IOBluetooth/IOBluetooth.h>
+
+#include "base/base_export.h"
#if !defined(MAC_OS_X_VERSION_10_7) || \
MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
enum {
NSEventPhaseNone = 0, // event not associated with a phase.
NSEventPhaseBegan = 0x1 << 0,
NSEventPhaseStationary = 0x1 << 1,
NSEventPhaseChanged = 0x1 << 2,
NSEventPhaseEnded = 0x1 << 3,
- NSEventPhaseCancelled = 0x1 << 4,
- NSEventPhaseMayBegin = 0x1 << 5
+ NSEventPhaseCancelled = 0x1 << 4
};
typedef NSUInteger NSEventPhase;
enum {
+ NSFullScreenWindowMask = 1 << 14,
+};
+
+enum {
+ NSApplicationPresentationFullScreen = 1 << 10,
+};
+
+enum {
+ NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
+ NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
+};
+
+enum {
NSEventSwipeTrackingLockDirection = 0x1 << 0,
NSEventSwipeTrackingClampGestureAmount = 0x1 << 1,
};
@@ -41,6 +59,12 @@ enum {
};
typedef NSInteger NSWindowAnimationBehavior;
+enum {
+ NSWindowDocumentVersionsButton = 6,
+ NSWindowFullScreenButton,
+};
+typedef NSUInteger NSWindowButton;
+
@interface NSEvent (LionSDK)
+ (BOOL)isSwipeTrackingFromScrollEventsEnabled;
@@ -60,7 +84,11 @@ typedef NSInteger NSWindowAnimationBehavior;
@end
-@interface CALayer (LionAPI)
+@interface NSApplication (LionSDK)
+- (void)disableRelaunchOnLogin;
+@end
+
+@interface CALayer (LionSDK)
- (CGFloat)contentsScale;
- (void)setContentsScale:(CGFloat)contentsScale;
@end
@@ -74,7 +102,167 @@ typedef NSInteger NSWindowAnimationBehavior;
- (CGFloat)backingScaleFactor;
- (NSWindowAnimationBehavior)animationBehavior;
- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
+- (void)toggleFullScreen:(id)sender;
+- (void)setRestorable:(BOOL)flag;
+@end
+
+@interface NSCursor (LionSDKDeclarations)
++ (NSCursor*)IBeamCursorForVerticalLayout;
+@end
+
+@interface NSAnimationContext (LionSDK)
++ (void)runAnimationGroup:(void (^)(NSAnimationContext *context))changes
+ completionHandler:(void (^)(void))completionHandler;
+@property(copy) void(^completionHandler)(void);
+@end
+
+@interface NSView (LionSDK)
+- (NSSize)convertSizeFromBacking:(NSSize)size;
+- (void)setWantsBestResolutionOpenGLSurface:(BOOL)flag;
+@end
+
+@interface NSObject (ICCameraDeviceDelegateLionSDK)
+- (void)deviceDidBecomeReadyWithCompleteContentCatalog:(ICDevice*)device;
+- (void)didDownloadFile:(ICCameraFile*)file
+ error:(NSError*)error
+ options:(NSDictionary*)options
+ contextInfo:(void*)contextInfo;
+@end
+
+@interface NSScroller (LionSDK)
++ (NSInteger)preferredScrollerStyle;
+@end
+
+@interface CWInterface (LionSDK)
+- (BOOL)associateToNetwork:(CWNetwork*)network
+ password:(NSString*)password
+ error:(NSError**)error;
+- (NSSet*)scanForNetworksWithName:(NSString*)networkName
+ error:(NSError**)error;
+@end
+
+enum CWChannelBand {
+ kCWChannelBandUnknown = 0,
+ kCWChannelBand2GHz = 1,
+ kCWChannelBand5GHz = 2,
+};
+
+@interface CWChannel : NSObject
+@property(readonly) CWChannelBand channelBand;
+@end
+
+@interface CWNetwork (LionSDK)
+@property(readonly) CWChannel* wlanChannel;
+@end
+
+@interface IOBluetoothHostController (LionSDK)
+- (NSString*)nameAsString;
+- (BluetoothHCIPowerState)powerState;
+@end
+
+@protocol IOBluetoothDeviceInquiryDelegate
+- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry*)sender;
+- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry*)sender
+ device:(IOBluetoothDevice*)device;
+- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry*)sender
+ error:(IOReturn)error
+ aborted:(BOOL)aborted;
+@end
+
+@interface IOBluetoothL2CAPChannel (LionSDK)
+@property(readonly) BluetoothL2CAPMTU outgoingMTU;
@end
+
+@interface IOBluetoothDevice (LionSDK)
+- (NSString*)addressString;
+- (unsigned int)classOfDevice;
+- (BluetoothConnectionHandle)connectionHandle;
+- (BluetoothHCIRSSIValue)rawRSSI;
+- (NSArray*)services;
+- (IOReturn)performSDPQuery:(id)target uuids:(NSArray*)uuids;
+@end
+
+BASE_EXPORT extern "C" NSString* const NSWindowWillEnterFullScreenNotification;
+
#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
+
+enum {
+ NSEventPhaseMayBegin = 0x1 << 5
+};
+
+#endif // MAC_OS_X_VERSION_10_8
+
+
+#if !defined(MAC_OS_X_VERSION_10_9) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9
+
+// NSProgress is public API in 10.9, but a version of it exists and is usable
+// in 10.8.
+
+@interface NSProgress : NSObject
+
+- (instancetype)initWithParent:(NSProgress*)parentProgressOrNil
+ userInfo:(NSDictionary*)userInfoOrNil;
+@property (copy) NSString* kind;
+
+@property int64_t totalUnitCount;
+@property int64_t completedUnitCount;
+
+@property (getter=isCancellable) BOOL cancellable;
+@property (getter=isPausable) BOOL pausable;
+@property (readonly, getter=isCancelled) BOOL cancelled;
+@property (readonly, getter=isPaused) BOOL paused;
+@property (copy) void (^cancellationHandler)(void);
+@property (copy) void (^pausingHandler)(void);
+- (void)cancel;
+- (void)pause;
+
+- (void)setUserInfoObject:(id)objectOrNil forKey:(NSString*)key;
+- (NSDictionary*)userInfo;
+
+@property (readonly, getter=isIndeterminate) BOOL indeterminate;
+@property (readonly) double fractionCompleted;
+
+- (void)publish;
+- (void)unpublish;
+
+@end
+
+@interface NSView (MavericksSDK)
+- (void)setCanDrawSubviewsIntoLayer:(BOOL)flag;
+@end
+
+enum {
+ NSWindowOcclusionStateVisible = 1UL << 1,
+};
+typedef NSUInteger NSWindowOcclusionState;
+
+@interface NSWindow (MavericksSDK)
+- (NSWindowOcclusionState)occlusionState;
+@end
+
+#endif // MAC_OS_X_VERSION_10_9
+
+
+#if !defined(MAC_OS_X_VERSION_10_10) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10
+
+enum {
+ NSWindowTitleVisible = 0,
+ NSWindowTitleHidden = 1,
+ NSWindowTitleHiddenWhenActive = 2,
+};
+typedef NSInteger NSWindowTitleVisibility;
+
+@interface NSWindow (YosemiteSDK)
+
+@property NSWindowTitleVisibility titleVisibility;
+
+@end
+
+#endif // MAC_OS_X_VERSION_10_10
+
#endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_
diff --git a/chromium/base/mac/sdk_forward_declarations.mm b/chromium/base/mac/sdk_forward_declarations.mm
new file mode 100644
index 00000000000..a402a417d85
--- /dev/null
+++ b/chromium/base/mac/sdk_forward_declarations.mm
@@ -0,0 +1,14 @@
+// Copyright 2014 The Chromium Authors. All 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/sdk_forward_declarations.h"
+
+// Replicate specific 10.7 SDK declarations for building with prior SDKs.
+#if !defined(MAC_OS_X_VERSION_10_7) || \
+ MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
+
+NSString* const NSWindowWillEnterFullScreenNotification =
+ @"NSWindowWillEnterFullScreenNotification";
+
+#endif // MAC_OS_X_VERSION_10_7
diff --git a/chromium/base/macros.h b/chromium/base/macros.h
new file mode 100644
index 00000000000..781751569d7
--- /dev/null
+++ b/chromium/base/macros.h
@@ -0,0 +1,313 @@
+// Copyright 2014 The Chromium 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 macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+#include <stddef.h> // For size_t.
+#include <string.h> // For memcpy.
+
+#include "base/compiler_specific.h" // For ALLOW_UNUSED.
+
+// Put this in the private: declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) \
+ TypeName(const TypeName&)
+
+// Put this in the private: declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) \
+ void operator=(const TypeName&)
+
+// 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 banned 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
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertible 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.
+
+#undef COMPILE_ASSERT
+
+#if __cplusplus >= 201103L
+
+// Under C++11, just use static_assert.
+#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+
+#else
+
+template <bool>
+struct CompileAssert {
+};
+
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ALLOW_UNUSED
+
+// 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.
+
+#endif
+
+// 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 between 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_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
+
+ 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_MACROS_H_
diff --git a/chromium/base/md5.cc b/chromium/base/md5.cc
index 754994c0e56..6227ee66042 100644
--- a/chromium/base/md5.cc
+++ b/chromium/base/md5.cc
@@ -251,8 +251,12 @@ void MD5Final(MD5Digest* digest, MD5Context* context) {
byteReverse(ctx->in, 14);
/* Append length in bits and transform */
- ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
- ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+ memcpy(&ctx->in[14 * sizeof(ctx->bits[0])],
+ &ctx->bits[0],
+ sizeof(ctx->bits[0]));
+ memcpy(&ctx->in[15 * sizeof(ctx->bits[1])],
+ &ctx->bits[1],
+ sizeof(ctx->bits[1]));
MD5Transform(ctx->buf, (uint32 *)ctx->in);
byteReverse((unsigned char *)ctx->buf, 4);
@@ -260,6 +264,14 @@ void MD5Final(MD5Digest* digest, MD5Context* context) {
memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */
}
+void MD5IntermediateFinal(MD5Digest* digest, const MD5Context* context) {
+ /* MD5Final mutates the MD5Context*. Make a copy for generating the
+ intermediate value. */
+ MD5Context context_copy;
+ memcpy(&context_copy, context, sizeof(context_copy));
+ MD5Final(digest, &context_copy);
+}
+
std::string MD5DigestToBase16(const MD5Digest& digest) {
static char const zEncode[] = "0123456789abcdef";
diff --git a/chromium/base/md5.h b/chromium/base/md5.h
index fba02bd1163..0a87fcf7c4a 100644
--- a/chromium/base/md5.h
+++ b/chromium/base/md5.h
@@ -58,6 +58,12 @@ BASE_EXPORT void MD5Update(MD5Context* context, const StringPiece& data);
// Finalizes the MD5 operation and fills the buffer with the digest.
BASE_EXPORT void MD5Final(MD5Digest* digest, MD5Context* context);
+// MD5IntermediateFinal() generates a digest without finalizing the MD5
+// operation. Can be used to generate digests for the input seen thus far,
+// without affecting the digest generated for the entire input.
+BASE_EXPORT void MD5IntermediateFinal(MD5Digest* digest,
+ const MD5Context* context);
+
// Converts a digest into human-readable hexadecimal.
BASE_EXPORT std::string MD5DigestToBase16(const MD5Digest& digest);
diff --git a/chromium/base/md5_unittest.cc b/chromium/base/md5_unittest.cc
index 1112c4b4258..3e7f2ad587d 100644
--- a/chromium/base/md5_unittest.cc
+++ b/chromium/base/md5_unittest.cc
@@ -204,4 +204,49 @@ TEST(MD5, ContextWithStringData) {
EXPECT_EQ(expected, actual);
}
+// Test that a digest generated by MD5IntermediateFinal() gives the same results
+// as an independently-calculated digest, and also does not modify the context.
+TEST(MD5, IntermediateFinal) {
+ // Independent context over the header.
+ MD5Context check_header_context;
+ MD5Init(&check_header_context);
+
+ // Independent context over entire input.
+ MD5Context check_full_context;
+ MD5Init(&check_full_context);
+
+ // Context intermediate digest will be calculated from.
+ MD5Context context;
+ MD5Init(&context);
+
+ static const char kHeader[] = "header data";
+ static const char kBody[] = "payload data";
+
+ MD5Update(&context, kHeader);
+ MD5Update(&check_header_context, kHeader);
+ MD5Update(&check_full_context, kHeader);
+
+ MD5Digest check_header_digest;
+ MD5Final(&check_header_digest, &check_header_context);
+
+ MD5Digest header_digest;
+ MD5IntermediateFinal(&header_digest, &context);
+
+ MD5Update(&context, kBody);
+ MD5Update(&check_full_context, kBody);
+
+ MD5Digest check_full_digest;
+ MD5Final(&check_full_digest, &check_full_context);
+
+ MD5Digest digest;
+ MD5Final(&digest, &context);
+
+ // The header and full digest pairs are the same, and they aren't the same as
+ // each other.
+ EXPECT_TRUE(!memcmp(&header_digest, &check_header_digest,
+ sizeof(header_digest)));
+ EXPECT_TRUE(!memcmp(&digest, &check_full_digest, sizeof(digest)));
+ EXPECT_FALSE(!memcmp(&digest, &header_digest, sizeof(digest)));
+}
+
} // namespace base
diff --git a/chromium/base/memory/aligned_memory.h b/chromium/base/memory/aligned_memory.h
index 6719599dc54..1a4cba9ad84 100644
--- a/chromium/base/memory/aligned_memory.h
+++ b/chromium/base/memory/aligned_memory.h
@@ -26,9 +26,9 @@
// // ... later, to release the memory:
// AlignedFree(my_array);
//
-// Or using scoped_ptr_malloc:
+// Or using scoped_ptr:
//
-// scoped_ptr_malloc<float, ScopedPtrAlignedFree> my_array(
+// scoped_ptr<float, AlignedFreeDeleter> my_array(
// static_cast<float*>(AlignedAlloc(size, alignment)));
#ifndef BASE_MEMORY_ALIGNED_MEMORY_H_
@@ -101,9 +101,9 @@ inline void AlignedFree(void* ptr) {
#endif
}
-// Helper class for use with scoped_ptr_malloc.
-class BASE_EXPORT ScopedPtrAlignedFree {
- public:
+// Deleter for use with scoped_ptr. E.g., use as
+// scoped_ptr<Foo, base::AlignedFreeDeleter> foo;
+struct AlignedFreeDeleter {
inline void operator()(void* ptr) const {
AlignedFree(ptr);
}
diff --git a/chromium/base/memory/aligned_memory_unittest.cc b/chromium/base/memory/aligned_memory_unittest.cc
index 6942249f5a4..5d681f9a388 100644
--- a/chromium/base/memory/aligned_memory_unittest.cc
+++ b/chromium/base/memory/aligned_memory_unittest.cc
@@ -41,6 +41,10 @@ TEST(AlignedMemoryTest, StackAlignment) {
EXPECT_ALIGNED(raw8.void_data(), 8);
EXPECT_ALIGNED(raw16.void_data(), 16);
+
+ // TODO(ios): __attribute__((aligned(X))) with X >= 128 does not works on
+ // the stack when building for arm64 on iOS, http://crbug.com/349003
+#if !(defined(OS_IOS) && defined(ARCH_CPU_ARM64))
EXPECT_ALIGNED(raw128.void_data(), 128);
// NaCl x86-64 compiler emits non-validating instructions for >128
@@ -60,7 +64,8 @@ TEST(AlignedMemoryTest, StackAlignment) {
EXPECT_EQ(4096u, ALIGNOF(raw4096));
EXPECT_ALIGNED(raw4096.void_data(), 4096);
#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM_FAMILY))
-#endif
+#endif // !(defined(OS_NACL) && defined(ARCH_CPU_X86_64))
+#endif // !(defined(OS_IOS) && defined(ARCH_CPU_ARM64))
}
TEST(AlignedMemoryTest, DynamicAllocation) {
@@ -86,7 +91,7 @@ TEST(AlignedMemoryTest, DynamicAllocation) {
}
TEST(AlignedMemoryTest, ScopedDynamicAllocation) {
- scoped_ptr_malloc<float, base::ScopedPtrAlignedFree> p(
+ scoped_ptr<float, base::AlignedFreeDeleter> p(
static_cast<float*>(base::AlignedAlloc(8, 8)));
EXPECT_TRUE(p.get());
EXPECT_ALIGNED(p.get(), 8);
diff --git a/chromium/base/memory/discardable_memory.cc b/chromium/base/memory/discardable_memory.cc
new file mode 100644
index 00000000000..9ba47aa6bfb
--- /dev/null
+++ b/chromium/base/memory/discardable_memory.cc
@@ -0,0 +1,86 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+namespace base {
+namespace {
+
+const struct TypeNamePair {
+ DiscardableMemoryType type;
+ const char* name;
+} kTypeNamePairs[] = {
+ { DISCARDABLE_MEMORY_TYPE_ASHMEM, "ashmem" },
+ { DISCARDABLE_MEMORY_TYPE_MAC, "mac" },
+ { DISCARDABLE_MEMORY_TYPE_EMULATED, "emulated" },
+ { DISCARDABLE_MEMORY_TYPE_MALLOC, "malloc" }
+};
+
+DiscardableMemoryType g_preferred_type = DISCARDABLE_MEMORY_TYPE_NONE;
+
+struct DefaultPreferredType {
+ DefaultPreferredType() : value(DISCARDABLE_MEMORY_TYPE_NONE) {
+ std::vector<DiscardableMemoryType> supported_types;
+ DiscardableMemory::GetSupportedTypes(&supported_types);
+ DCHECK(!supported_types.empty());
+ value = supported_types[0];
+ }
+ DiscardableMemoryType value;
+};
+LazyInstance<DefaultPreferredType>::Leaky g_default_preferred_type =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// static
+DiscardableMemoryType DiscardableMemory::GetNamedType(
+ const std::string& name) {
+ for (size_t i = 0; i < arraysize(kTypeNamePairs); ++i) {
+ if (name == kTypeNamePairs[i].name)
+ return kTypeNamePairs[i].type;
+ }
+
+ return DISCARDABLE_MEMORY_TYPE_NONE;
+}
+
+// static
+const char* DiscardableMemory::GetTypeName(DiscardableMemoryType type) {
+ for (size_t i = 0; i < arraysize(kTypeNamePairs); ++i) {
+ if (type == kTypeNamePairs[i].type)
+ return kTypeNamePairs[i].name;
+ }
+
+ return "unknown";
+}
+
+// static
+void DiscardableMemory::SetPreferredType(DiscardableMemoryType type) {
+ // NONE is a reserved value and not a valid default type.
+ DCHECK_NE(DISCARDABLE_MEMORY_TYPE_NONE, type);
+
+ // Make sure this function is only called once before the first call
+ // to GetPreferredType().
+ DCHECK_EQ(DISCARDABLE_MEMORY_TYPE_NONE, g_preferred_type);
+
+ g_preferred_type = type;
+}
+
+// static
+DiscardableMemoryType DiscardableMemory::GetPreferredType() {
+ if (g_preferred_type == DISCARDABLE_MEMORY_TYPE_NONE)
+ g_preferred_type = g_default_preferred_type.Get().value;
+
+ return g_preferred_type;
+}
+
+// static
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
+ size_t size) {
+ return CreateLockedMemoryWithType(GetPreferredType(), size);
+}
+
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory.h b/chromium/base/memory/discardable_memory.h
index cbc2db630a8..d16ed3e8480 100644
--- a/chromium/base/memory/discardable_memory.h
+++ b/chromium/base/memory/discardable_memory.h
@@ -5,6 +5,9 @@
#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_H_
#define BASE_MEMORY_DISCARDABLE_MEMORY_H_
+#include <string>
+#include <vector>
+
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
@@ -12,10 +15,18 @@
namespace base {
-enum LockDiscardableMemoryStatus {
- DISCARDABLE_MEMORY_FAILED = -1,
- DISCARDABLE_MEMORY_PURGED = 0,
- DISCARDABLE_MEMORY_SUCCESS = 1
+enum DiscardableMemoryType {
+ DISCARDABLE_MEMORY_TYPE_NONE,
+ DISCARDABLE_MEMORY_TYPE_ASHMEM,
+ DISCARDABLE_MEMORY_TYPE_MAC,
+ DISCARDABLE_MEMORY_TYPE_EMULATED,
+ DISCARDABLE_MEMORY_TYPE_MALLOC
+};
+
+enum DiscardableMemoryLockStatus {
+ DISCARDABLE_MEMORY_LOCK_STATUS_FAILED,
+ DISCARDABLE_MEMORY_LOCK_STATUS_PURGED,
+ DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS
};
// Platform abstraction for discardable memory. DiscardableMemory is used to
@@ -53,19 +64,56 @@ class BASE_EXPORT DiscardableMemory {
public:
virtual ~DiscardableMemory() {}
- // Check whether the system supports discardable memory natively. Returns
- // false if the support is emulated.
- static bool SupportedNatively();
+ // Call this on a thread with a MessageLoop current to allow discardable
+ // memory implementations to respond to memory pressure signals.
+ static void RegisterMemoryPressureListeners();
+
+ // Call this to prevent discardable memory implementations from responding
+ // to memory pressure signals.
+ static void UnregisterMemoryPressureListeners();
+
+ // Gets the discardable memory type with a given name.
+ static DiscardableMemoryType GetNamedType(const std::string& name);
+
+ // Gets the name of a discardable memory type.
+ static const char* GetTypeName(DiscardableMemoryType type);
+ // Gets system supported discardable memory types. Default preferred type
+ // at the front of vector.
+ static void GetSupportedTypes(std::vector<DiscardableMemoryType>* types);
+
+ // Sets the preferred discardable memory type. This overrides the default
+ // preferred type. Can only be called once prior to GetPreferredType()
+ // or CreateLockedMemory(). Caller is responsible for correct ordering.
+ static void SetPreferredType(DiscardableMemoryType type);
+
+ // Gets the preferred discardable memory type.
+ static DiscardableMemoryType GetPreferredType();
+
+ // Create a DiscardableMemory instance with specified |type| and |size|.
+ static scoped_ptr<DiscardableMemory> CreateLockedMemoryWithType(
+ DiscardableMemoryType type, size_t size);
+
+ // Create a DiscardableMemory instance with preferred type and |size|.
static scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size);
+ // Discardable memory implementations might allow an elevated usage level
+ // while in frequent use. Call this to have the usage reduced to the base
+ // level. Returns true if there's no need to call this again until
+ // memory instances have been used. This indicates that all discardable
+ // memory implementations have reduced usage to the base level or below.
+ // Note: calling this too often or while discardable memory is in frequent
+ // use can hurt performance, whereas calling it too infrequently can result
+ // in memory bloat.
+ static bool ReduceMemoryUsage();
+
// Locks the memory so that it will not be purged by the system. Returns
- // DISCARDABLE_MEMORY_SUCCESS on success. If the return value is
- // DISCARDABLE_MEMORY_FAILED then this object should be discarded and
- // a new one should be created. If the return value is
- // DISCARDABLE_MEMORY_PURGED then the memory is present but any data that
- // was in it is gone.
- virtual LockDiscardableMemoryStatus Lock() WARN_UNUSED_RESULT = 0;
+ // DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS on success. If the return value is
+ // DISCARDABLE_MEMORY_LOCK_STATUS_FAILED then this object should be
+ // discarded and a new one should be created. If the return value is
+ // DISCARDABLE_MEMORY_LOCK_STATUS_PURGED then the memory is present but any
+ // data that was in it is gone.
+ virtual DiscardableMemoryLockStatus Lock() WARN_UNUSED_RESULT = 0;
// Unlocks the memory so that it can be purged by the system. Must be called
// after every successful lock call.
@@ -77,10 +125,6 @@ class BASE_EXPORT DiscardableMemory {
// Testing utility calls.
- // Check whether a purge of all discardable memory in the system is supported.
- // Use only for testing!
- static bool PurgeForTestingSupported();
-
// Purge all discardable memory in the system. This call has global effects
// across all running processes, so it should only be used for testing!
static void PurgeForTesting();
diff --git a/chromium/base/memory/discardable_memory_allocator_android.h b/chromium/base/memory/discardable_memory_allocator_android.h
deleted file mode 100644
index 7991656cff6..00000000000
--- a/chromium/base/memory/discardable_memory_allocator_android.h
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 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_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
-#define BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
-
-namespace base {
-
-class DiscardableMemory;
-
-namespace internal {
-
-// On Android ashmem is used to implement discardable memory. It is backed by a
-// file (descriptor) thus is a limited resource. This allocator minimizes the
-// problem by allocating large ashmem regions internally and returning smaller
-// chunks to the client.
-// Allocated chunks are systematically aligned on a page boundary therefore this
-// allocator should not be used for small allocations.
-//
-// Threading: The allocator must be deleted on the thread it was constructed on
-// although its Allocate() method can be invoked on any thread. See
-// discardable_memory.h for DiscardableMemory's threading guarantees.
-class BASE_EXPORT_PRIVATE DiscardableMemoryAllocator {
- public:
- // Exposed for testing.
- enum {
- kMinAshmemRegionSize = 32 * 1024 * 1024,
- };
-
- // Note that |name| is only used for debugging/measurement purposes.
- explicit DiscardableMemoryAllocator(const std::string& name);
- ~DiscardableMemoryAllocator();
-
- // Note that the allocator must outlive the returned DiscardableMemory
- // instance.
- scoped_ptr<DiscardableMemory> Allocate(size_t size);
-
- private:
- class AshmemRegion;
- class DiscardableAshmemChunk;
-
- void DeleteAshmemRegion_Locked(AshmemRegion* region);
-
- base::ThreadChecker thread_checker_;
- const std::string name_;
- base::Lock lock_;
- ScopedVector<AshmemRegion> ashmem_regions_;
-
- DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAllocator);
-};
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ALLOCATOR_H_
diff --git a/chromium/base/memory/discardable_memory_allocator_android_unittest.cc b/chromium/base/memory/discardable_memory_allocator_android_unittest.cc
deleted file mode 100644
index 97cf5d45f72..00000000000
--- a/chromium/base/memory/discardable_memory_allocator_android_unittest.cc
+++ /dev/null
@@ -1,232 +0,0 @@
-// Copyright 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.
-
-#include "base/memory/discardable_memory_allocator_android.h"
-
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "base/memory/discardable_memory.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/strings/string_number_conversions.h"
-#include "base/strings/string_split.h"
-#include "base/strings/stringprintf.h"
-#include "build/build_config.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace internal {
-
-const char kAllocatorName[] = "allocator-for-testing";
-
-const size_t kPageSize = 4096;
-const size_t kMinAshmemRegionSize =
- DiscardableMemoryAllocator::kMinAshmemRegionSize;
-
-class DiscardableMemoryAllocatorTest : public testing::Test {
- protected:
- DiscardableMemoryAllocatorTest() : allocator_(kAllocatorName) {}
-
- DiscardableMemoryAllocator allocator_;
-};
-
-void WriteToDiscardableMemory(DiscardableMemory* memory, size_t size) {
- // Write to the first and the last pages only to avoid paging in up to 64
- // MBytes.
- static_cast<char*>(memory->Memory())[0] = 'a';
- static_cast<char*>(memory->Memory())[size - 1] = 'a';
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, Basic) {
- const size_t size = 128;
- scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(size));
- ASSERT_TRUE(memory);
- WriteToDiscardableMemory(memory.get(), size);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, LargeAllocation) {
- // Note that large allocations should just use DiscardableMemoryAndroidSimple
- // instead.
- const size_t size = 64 * 1024 * 1024;
- scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(size));
- ASSERT_TRUE(memory);
- WriteToDiscardableMemory(memory.get(), size);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, ChunksArePageAligned) {
- scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory);
- EXPECT_EQ(0U, reinterpret_cast<uint64_t>(memory->Memory()) % kPageSize);
- WriteToDiscardableMemory(memory.get(), kPageSize);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, AllocateFreeAllocate) {
- scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(kPageSize));
- // Extra allocation that prevents the region from being deleted when |memory|
- // gets deleted.
- scoped_ptr<DiscardableMemory> memory_lock(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory);
- void* const address = memory->Memory();
- memory->Unlock(); // Tests that the reused chunk is being locked correctly.
- memory.reset();
- memory = allocator_.Allocate(kPageSize);
- ASSERT_TRUE(memory);
- // The previously freed chunk should be reused.
- EXPECT_EQ(address, memory->Memory());
- WriteToDiscardableMemory(memory.get(), kPageSize);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, FreeingWholeAshmemRegionClosesAshmem) {
- scoped_ptr<DiscardableMemory> memory(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory);
- const int kMagic = 0xdeadbeef;
- *static_cast<int*>(memory->Memory()) = kMagic;
- memory.reset();
- // The previous ashmem region should have been closed thus it should not be
- // reused.
- memory = allocator_.Allocate(kPageSize);
- ASSERT_TRUE(memory);
- EXPECT_NE(kMagic, *static_cast<const int*>(memory->Memory()));
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, AllocateUsesBestFitAlgorithm) {
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(3 * kPageSize));
- ASSERT_TRUE(memory1);
- scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(2 * kPageSize));
- ASSERT_TRUE(memory2);
- scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(1 * kPageSize));
- ASSERT_TRUE(memory3);
- void* const address_3 = memory3->Memory();
- memory1.reset();
- // Don't free |memory2| to avoid merging the 3 blocks together.
- memory3.reset();
- memory1 = allocator_.Allocate(1 * kPageSize);
- ASSERT_TRUE(memory1);
- // The chunk whose size is closest to the requested size should be reused.
- EXPECT_EQ(address_3, memory1->Memory());
- WriteToDiscardableMemory(memory1.get(), kPageSize);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, MergeFreeChunks) {
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory1);
- scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory2);
- scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory3);
- scoped_ptr<DiscardableMemory> memory4(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory4);
- void* const memory1_address = memory1->Memory();
- memory1.reset();
- memory3.reset();
- // Freeing |memory2| (located between memory1 and memory3) should merge the
- // three free blocks together.
- memory2.reset();
- memory1 = allocator_.Allocate(3 * kPageSize);
- EXPECT_EQ(memory1_address, memory1->Memory());
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, MergeFreeChunksAdvanced) {
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory1);
- scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory2);
- void* const memory1_address = memory1->Memory();
- memory1.reset();
- memory1 = allocator_.Allocate(2 * kPageSize);
- memory2.reset();
- // At this point, the region should be in this state:
- // 8 KBytes (used), 24 KBytes (free).
- memory2 = allocator_.Allocate(6 * kPageSize);
- EXPECT_EQ(
- static_cast<const char*>(memory2->Memory()),
- static_cast<const char*>(memory1_address) + 2 * kPageSize);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, MergeFreeChunksAdvanced2) {
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory1);
- scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory2);
- void* const memory1_address = memory1->Memory();
- memory1.reset();
- memory1 = allocator_.Allocate(2 * kPageSize);
- scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(2 * kPageSize));
- // At this point, the region should be in this state:
- // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used).
- memory3.reset();
- memory2.reset();
- // At this point, the region should be in this state:
- // 8 KBytes (used), 24 KBytes (free).
- memory2 = allocator_.Allocate(6 * kPageSize);
- EXPECT_EQ(
- static_cast<const char*>(memory2->Memory()),
- static_cast<const char*>(memory1_address) + 2 * kPageSize);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, MergeFreeChunksAndDeleteAshmemRegion) {
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory1);
- scoped_ptr<DiscardableMemory> memory2(allocator_.Allocate(4 * kPageSize));
- ASSERT_TRUE(memory2);
- memory1.reset();
- memory1 = allocator_.Allocate(2 * kPageSize);
- scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(2 * kPageSize));
- // At this point, the region should be in this state:
- // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used).
- memory1.reset();
- memory3.reset();
- // At this point, the region should be in this state:
- // 8 KBytes (free), 8 KBytes (used), 8 KBytes (free).
- const int kMagic = 0xdeadbeef;
- *static_cast<int*>(memory2->Memory()) = kMagic;
- memory2.reset();
- // The whole region should have been deleted.
- memory2 = allocator_.Allocate(2 * kPageSize);
- EXPECT_NE(kMagic, *static_cast<int*>(memory2->Memory()));
-}
-
-TEST_F(DiscardableMemoryAllocatorTest,
- TooLargeFreeChunksDontCauseTooMuchFragmentationWhenRecycled) {
- // Keep |memory_1| below allocated so that the ashmem region doesn't get
- // closed when |memory_2| is deleted.
- scoped_ptr<DiscardableMemory> memory_1(allocator_.Allocate(64 * 1024));
- ASSERT_TRUE(memory_1);
- scoped_ptr<DiscardableMemory> memory_2(allocator_.Allocate(32 * 1024));
- ASSERT_TRUE(memory_2);
- void* const address = memory_2->Memory();
- memory_2.reset();
- const size_t size = 16 * 1024;
- memory_2 = allocator_.Allocate(size);
- ASSERT_TRUE(memory_2);
- EXPECT_EQ(address, memory_2->Memory());
- WriteToDiscardableMemory(memory_2.get(), size);
- scoped_ptr<DiscardableMemory> memory_3(allocator_.Allocate(size));
- // The unused tail (16 KBytes large) of the previously freed chunk should be
- // reused.
- EXPECT_EQ(static_cast<char*>(address) + size, memory_3->Memory());
- WriteToDiscardableMemory(memory_3.get(), size);
-}
-
-TEST_F(DiscardableMemoryAllocatorTest, UseMultipleAshmemRegions) {
- // Leave one page untouched at the end of the ashmem region.
- const size_t size = kMinAshmemRegionSize - kPageSize;
- scoped_ptr<DiscardableMemory> memory1(allocator_.Allocate(size));
- ASSERT_TRUE(memory1);
- WriteToDiscardableMemory(memory1.get(), size);
-
- scoped_ptr<DiscardableMemory> memory2(
- allocator_.Allocate(kMinAshmemRegionSize));
- ASSERT_TRUE(memory2);
- WriteToDiscardableMemory(memory2.get(), kMinAshmemRegionSize);
- // The last page of the first ashmem region should be used for this
- // allocation.
- scoped_ptr<DiscardableMemory> memory3(allocator_.Allocate(kPageSize));
- ASSERT_TRUE(memory3);
- WriteToDiscardableMemory(memory3.get(), kPageSize);
- EXPECT_EQ(memory3->Memory(), static_cast<char*>(memory1->Memory()) + size);
-}
-
-} // namespace internal
-} // namespace base
diff --git a/chromium/base/memory/discardable_memory_android.cc b/chromium/base/memory/discardable_memory_android.cc
index 7e84967055f..33d38085716 100644
--- a/chromium/base/memory/discardable_memory_android.cc
+++ b/chromium/base/memory/discardable_memory_android.cc
@@ -2,258 +2,119 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/discardable_memory_android.h"
-
-#include <sys/mman.h>
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <limits>
+#include "base/memory/discardable_memory.h"
+#include "base/android/sys_utils.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
-#include "base/file_util.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/discardable_memory.h"
-#include "base/memory/discardable_memory_allocator_android.h"
-#include "base/synchronization/lock.h"
-#include "third_party/ashmem/ashmem.h"
+#include "base/memory/discardable_memory_ashmem.h"
+#include "base/memory/discardable_memory_ashmem_allocator.h"
+#include "base/memory/discardable_memory_emulated.h"
+#include "base/memory/discardable_memory_malloc.h"
namespace base {
namespace {
-const size_t kPageSize = 4096;
-
-const char kAshmemAllocatorName[] = "DiscardableMemoryAllocator";
-
-struct GlobalContext {
- GlobalContext()
- : ashmem_fd_limit(GetSoftFDLimit()),
- allocator(kAshmemAllocatorName),
- ashmem_fd_count_(0) {
- }
-
- const int ashmem_fd_limit;
- internal::DiscardableMemoryAllocator allocator;
- Lock lock;
-
- int ashmem_fd_count() const {
- lock.AssertAcquired();
- return ashmem_fd_count_;
- }
-
- void decrement_ashmem_fd_count() {
- lock.AssertAcquired();
- --ashmem_fd_count_;
- }
-
- void increment_ashmem_fd_count() {
- lock.AssertAcquired();
- ++ashmem_fd_count_;
- }
-
- private:
- static int GetSoftFDLimit() {
- struct rlimit limit_info;
- if (getrlimit(RLIMIT_NOFILE, &limit_info) != 0)
- return 128;
- // Allow 25% of file descriptor capacity for ashmem.
- return limit_info.rlim_cur / 4;
- }
-
- int ashmem_fd_count_;
-};
-
-LazyInstance<GlobalContext>::Leaky g_context = LAZY_INSTANCE_INITIALIZER;
-
-// This is the default implementation of DiscardableMemory on Android which is
-// used when file descriptor usage is under the soft limit. When file descriptor
-// usage gets too high the discardable memory allocator is used instead. See
-// ShouldUseAllocator() below for more details.
-class DiscardableMemoryAndroidSimple : public DiscardableMemory {
- public:
- DiscardableMemoryAndroidSimple(int fd, void* address, size_t size)
- : fd_(fd),
- memory_(address),
- size_(size) {
- DCHECK_GE(fd_, 0);
- DCHECK(memory_);
- }
-
- virtual ~DiscardableMemoryAndroidSimple() {
- internal::CloseAshmemRegion(fd_, size_, memory_);
- }
-
- // DiscardableMemory:
- virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
- return internal::LockAshmemRegion(fd_, 0, size_, memory_);
- }
+const char kAshmemAllocatorName[] = "DiscardableMemoryAshmemAllocator";
- virtual void Unlock() OVERRIDE {
- internal::UnlockAshmemRegion(fd_, 0, size_, memory_);
- }
-
- virtual void* Memory() const OVERRIDE {
- return memory_;
- }
+// For Ashmem, have the DiscardableMemoryManager trigger userspace eviction
+// when address space usage gets too high (e.g. 512 MBytes).
+const size_t kAshmemMemoryLimit = 512 * 1024 * 1024;
- private:
- const int fd_;
- void* const memory_;
- const size_t size_;
-
- DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAndroidSimple);
-};
-
-int GetCurrentNumberOfAshmemFDs() {
- AutoLock lock(g_context.Get().lock);
- return g_context.Get().ashmem_fd_count();
+size_t GetOptimalAshmemRegionSizeForAllocator() {
+ // Note that this may do some I/O (without hitting the disk though) so it
+ // should not be called on the critical path.
+ return base::android::SysUtils::AmountOfPhysicalMemoryKB() * 1024 / 8;
}
-// Returns whether the provided size can be safely page-aligned (without causing
-// an overflow).
-bool CheckSizeCanBeAlignedToNextPage(size_t size) {
- return size <= std::numeric_limits<size_t>::max() - kPageSize + 1;
-}
+// Holds the shared state used for allocations.
+struct SharedState {
+ SharedState()
+ : manager(kAshmemMemoryLimit,
+ kAshmemMemoryLimit,
+ kAshmemMemoryLimit,
+ TimeDelta::Max()),
+ allocator(kAshmemAllocatorName,
+ GetOptimalAshmemRegionSizeForAllocator()) {}
+
+ internal::DiscardableMemoryManager manager;
+ internal::DiscardableMemoryAshmemAllocator allocator;
+};
+LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER;
} // namespace
-namespace internal {
-
-size_t AlignToNextPage(size_t size) {
- DCHECK_EQ(static_cast<int>(kPageSize), getpagesize());
- DCHECK(CheckSizeCanBeAlignedToNextPage(size));
- const size_t mask = ~(kPageSize - 1);
- return (size + kPageSize - 1) & mask;
-}
-
-bool CreateAshmemRegion(const char* name,
- size_t size,
- int* out_fd,
- void** out_address) {
- AutoLock lock(g_context.Get().lock);
- if (g_context.Get().ashmem_fd_count() + 1 > g_context.Get().ashmem_fd_limit)
- return false;
- int fd = ashmem_create_region(name, size);
- if (fd < 0) {
- DLOG(ERROR) << "ashmem_create_region() failed";
- return false;
- }
- file_util::ScopedFD fd_closer(&fd);
-
- const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
- if (err < 0) {
- DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
- return false;
- }
-
- // There is a problem using MAP_PRIVATE here. As we are constantly calling
- // Lock() and Unlock(), data could get lost if they are not written to the
- // underlying file when Unlock() gets called.
- void* const address = mmap(
- NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (address == MAP_FAILED) {
- DPLOG(ERROR) << "Failed to map memory.";
- return false;
- }
-
- ignore_result(fd_closer.release());
- g_context.Get().increment_ashmem_fd_count();
- *out_fd = fd;
- *out_address = address;
- return true;
-}
-
-bool CloseAshmemRegion(int fd, size_t size, void* address) {
- AutoLock lock(g_context.Get().lock);
- g_context.Get().decrement_ashmem_fd_count();
- if (munmap(address, size) == -1) {
- DPLOG(ERROR) << "Failed to unmap memory.";
- close(fd);
- return false;
- }
- return close(fd) == 0;
+// static
+void DiscardableMemory::RegisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners();
}
-LockDiscardableMemoryStatus LockAshmemRegion(int fd,
- size_t off,
- size_t size,
- const void* address) {
- const int result = ashmem_pin_region(fd, off, size);
- DCHECK_EQ(0, mprotect(address, size, PROT_READ | PROT_WRITE));
- return result == ASHMEM_WAS_PURGED ?
- DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
+// static
+void DiscardableMemory::UnregisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners();
}
-bool UnlockAshmemRegion(int fd, size_t off, size_t size, const void* address) {
- const int failed = ashmem_unpin_region(fd, off, size);
- if (failed)
- DLOG(ERROR) << "Failed to unpin memory.";
- // This allows us to catch accesses to unlocked memory.
- DCHECK_EQ(0, mprotect(address, size, PROT_NONE));
- return !failed;
+// static
+bool DiscardableMemory::ReduceMemoryUsage() {
+ return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
}
-} // namespace internal
-
// static
-bool DiscardableMemory::SupportedNatively() {
- return true;
+void DiscardableMemory::GetSupportedTypes(
+ std::vector<DiscardableMemoryType>* types) {
+ const DiscardableMemoryType supported_types[] = {
+ DISCARDABLE_MEMORY_TYPE_ASHMEM,
+ DISCARDABLE_MEMORY_TYPE_EMULATED,
+ DISCARDABLE_MEMORY_TYPE_MALLOC
+ };
+ types->assign(supported_types, supported_types + arraysize(supported_types));
}
-// Allocation can happen in two ways:
-// - Each client-requested allocation is backed by an individual ashmem region.
-// This allows deleting ashmem regions individually by closing the ashmem file
-// descriptor. This is the default path that is taken when file descriptor usage
-// allows us to do so or when the allocation size would require and entire
-// ashmem region.
-// - Allocations are performed by the global allocator when file descriptor
-// usage gets too high. This still allows unpinning but does not allow deleting
-// (i.e. releasing the physical pages backing) individual regions.
-//
-// TODO(pliard): consider tuning the size threshold used below. For instance we
-// might want to make it a fraction of kMinAshmemRegionSize and also
-// systematically have small allocations go through the allocator to let big
-// allocations systematically go through individual ashmem regions.
-//
// static
-scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
- size_t size) {
- if (!CheckSizeCanBeAlignedToNextPage(size))
- return scoped_ptr<DiscardableMemory>();
- // Pinning & unpinning works with page granularity therefore align the size
- // upfront.
- const size_t aligned_size = internal::AlignToNextPage(size);
- // Note that the following code is slightly racy. The worst that can happen in
- // practice though is taking the wrong decision (e.g. using the allocator
- // rather than DiscardableMemoryAndroidSimple). Moreover keeping the lock
- // acquired for the whole allocation would cause a deadlock when the allocator
- // tries to create an ashmem region.
- const size_t kAllocatorRegionSize =
- internal::DiscardableMemoryAllocator::kMinAshmemRegionSize;
- GlobalContext* const global_context = g_context.Pointer();
- if (aligned_size >= kAllocatorRegionSize ||
- GetCurrentNumberOfAshmemFDs() < 0.9 * global_context->ashmem_fd_limit) {
- int fd;
- void* address;
- if (internal::CreateAshmemRegion("", aligned_size, &fd, &address)) {
- return scoped_ptr<DiscardableMemory>(
- new DiscardableMemoryAndroidSimple(fd, address, aligned_size));
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
+ DiscardableMemoryType type, size_t size) {
+ switch (type) {
+ case DISCARDABLE_MEMORY_TYPE_NONE:
+ case DISCARDABLE_MEMORY_TYPE_MAC:
+ return scoped_ptr<DiscardableMemory>();
+ case DISCARDABLE_MEMORY_TYPE_ASHMEM: {
+ SharedState* const shared_state = g_shared_state.Pointer();
+ scoped_ptr<internal::DiscardableMemoryAshmem> memory(
+ new internal::DiscardableMemoryAshmem(
+ size, &shared_state->allocator, &shared_state->manager));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_EMULATED: {
+ scoped_ptr<internal::DiscardableMemoryEmulated> memory(
+ new internal::DiscardableMemoryEmulated(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_MALLOC: {
+ scoped_ptr<internal::DiscardableMemoryMalloc> memory(
+ new internal::DiscardableMemoryMalloc(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
}
}
- return global_context->allocator.Allocate(size);
-}
-// static
-bool DiscardableMemory::PurgeForTestingSupported() {
- return false;
+ NOTREACHED();
+ return scoped_ptr<DiscardableMemory>();
}
// static
void DiscardableMemory::PurgeForTesting() {
- NOTIMPLEMENTED();
+ g_shared_state.Pointer()->manager.PurgeAll();
+ internal::DiscardableMemoryEmulated::PurgeForTesting();
}
} // namespace base
diff --git a/chromium/base/memory/discardable_memory_android.h b/chromium/base/memory/discardable_memory_android.h
deleted file mode 100644
index 9db78b33853..00000000000
--- a/chromium/base/memory/discardable_memory_android.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 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.
-
-// Please use discardable_memory.h since this is just an internal file providing
-// utility functions used both by discardable_memory_android.cc and
-// discardable_memory_allocator_android.cc.
-
-#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_
-#define BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_
-
-#include "base/basictypes.h"
-#include "base/memory/discardable_memory.h"
-
-namespace base {
-namespace internal {
-
-size_t AlignToNextPage(size_t size);
-
-bool CreateAshmemRegion(const char* name, size_t size, int* fd, void** address);
-
-bool CloseAshmemRegion(int fd, size_t size, void* address);
-
-LockDiscardableMemoryStatus LockAshmemRegion(int fd,
- size_t offset,
- size_t size,
- const void* address);
-
-bool UnlockAshmemRegion(int fd,
- size_t offset,
- size_t size,
- const void* address);
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ANDROID_H_
diff --git a/chromium/base/memory/discardable_memory_ashmem.cc b/chromium/base/memory/discardable_memory_ashmem.cc
new file mode 100644
index 00000000000..a590e531b7a
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_ashmem.cc
@@ -0,0 +1,75 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_ashmem.h"
+
+#include "base/memory/discardable_memory_ashmem_allocator.h"
+
+namespace base {
+namespace internal {
+
+DiscardableMemoryAshmem::DiscardableMemoryAshmem(
+ size_t bytes,
+ DiscardableMemoryAshmemAllocator* allocator,
+ DiscardableMemoryManager* manager)
+ : bytes_(bytes),
+ allocator_(allocator),
+ manager_(manager),
+ is_locked_(false) {
+ manager_->Register(this, bytes_);
+}
+
+DiscardableMemoryAshmem::~DiscardableMemoryAshmem() {
+ if (is_locked_)
+ Unlock();
+
+ manager_->Unregister(this);
+}
+
+bool DiscardableMemoryAshmem::Initialize() {
+ return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
+}
+
+DiscardableMemoryLockStatus DiscardableMemoryAshmem::Lock() {
+ DCHECK(!is_locked_);
+
+ bool purged = false;
+ if (!manager_->AcquireLock(this, &purged))
+ return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
+
+ is_locked_ = true;
+ return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
+ : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
+}
+
+void DiscardableMemoryAshmem::Unlock() {
+ DCHECK(is_locked_);
+ manager_->ReleaseLock(this);
+ is_locked_ = false;
+}
+
+void* DiscardableMemoryAshmem::Memory() const {
+ DCHECK(is_locked_);
+ DCHECK(ashmem_chunk_);
+ return ashmem_chunk_->Memory();
+}
+
+bool DiscardableMemoryAshmem::AllocateAndAcquireLock() {
+ if (ashmem_chunk_)
+ return ashmem_chunk_->Lock();
+
+ ashmem_chunk_ = allocator_->Allocate(bytes_);
+ return false;
+}
+
+void DiscardableMemoryAshmem::ReleaseLock() {
+ ashmem_chunk_->Unlock();
+}
+
+void DiscardableMemoryAshmem::Purge() {
+ ashmem_chunk_.reset();
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_ashmem.h b/chromium/base/memory/discardable_memory_ashmem.h
new file mode 100644
index 00000000000..8436f5d8ece
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_ashmem.h
@@ -0,0 +1,55 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_
+
+#include "base/memory/discardable_memory.h"
+
+#include "base/macros.h"
+#include "base/memory/discardable_memory_manager.h"
+
+namespace base {
+namespace internal {
+
+class DiscardableAshmemChunk;
+class DiscardableMemoryAshmemAllocator;
+class DiscardableMemoryManager;
+
+class DiscardableMemoryAshmem
+ : public DiscardableMemory,
+ public internal::DiscardableMemoryManagerAllocation {
+ public:
+ explicit DiscardableMemoryAshmem(size_t bytes,
+ DiscardableMemoryAshmemAllocator* allocator,
+ DiscardableMemoryManager* manager);
+
+ virtual ~DiscardableMemoryAshmem();
+
+ bool Initialize();
+
+ // Overridden from DiscardableMemory:
+ virtual DiscardableMemoryLockStatus Lock() OVERRIDE;
+ virtual void Unlock() OVERRIDE;
+ virtual void* Memory() const OVERRIDE;
+
+ // Overridden from internal::DiscardableMemoryManagerAllocation:
+ virtual bool AllocateAndAcquireLock() OVERRIDE;
+ virtual void ReleaseLock() OVERRIDE;
+ virtual void Purge() OVERRIDE;
+
+ private:
+ const size_t bytes_;
+ DiscardableMemoryAshmemAllocator* const allocator_;
+ DiscardableMemoryManager* const manager_;
+ bool is_locked_;
+ scoped_ptr<DiscardableAshmemChunk> ashmem_chunk_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAshmem);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_H_
diff --git a/chromium/base/memory/discardable_memory_allocator_android.cc b/chromium/base/memory/discardable_memory_ashmem_allocator.cc
index 5e108176fe0..bc8c9b90617 100644
--- a/chromium/base/memory/discardable_memory_allocator_android.cc
+++ b/chromium/base/memory/discardable_memory_ashmem_allocator.cc
@@ -1,33 +1,34 @@
-// Copyright 2013 The Chromium Authors. All rights reserved.
+// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/memory/discardable_memory_allocator_android.h"
+#include "base/memory/discardable_memory_ashmem_allocator.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
#include <algorithm>
#include <cmath>
+#include <limits>
#include <set>
#include <utility>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
+#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
-#include "base/memory/discardable_memory.h"
-#include "base/memory/discardable_memory_android.h"
#include "base/memory/scoped_vector.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/thread_checker.h"
+#include "third_party/ashmem/ashmem.h"
// The allocator consists of three parts (classes):
-// - DiscardableMemoryAllocator: entry point of all allocations (through its
-// Allocate() method) that are dispatched to the AshmemRegion instances (which
-// it owns).
+// - DiscardableMemoryAshmemAllocator: entry point of all allocations (through
+// its Allocate() method) that are dispatched to the AshmemRegion instances
+// (which it owns).
// - AshmemRegion: manages allocations and destructions inside a single large
// (e.g. 32 MBytes) ashmem region.
-// - DiscardableAshmemChunk: class implementing the DiscardableMemory interface
-// whose instances are returned to the client. DiscardableAshmemChunk lets the
-// client seamlessly operate on a subrange of the ashmem region managed by
-// AshmemRegion.
+// - DiscardableAshmemChunk: class mimicking the DiscardableMemory interface
+// whose instances are returned to the client.
namespace base {
namespace {
@@ -44,80 +45,96 @@ namespace {
// issues.
const size_t kMaxChunkFragmentationBytes = 4096 - 1;
-} // namespace
+const size_t kMinAshmemRegionSize = 32 * 1024 * 1024;
-namespace internal {
+// Returns 0 if the provided size is too high to be aligned.
+size_t AlignToNextPage(size_t size) {
+ const size_t kPageSize = 4096;
+ DCHECK_EQ(static_cast<int>(kPageSize), getpagesize());
+ if (size > std::numeric_limits<size_t>::max() - kPageSize + 1)
+ return 0;
+ const size_t mask = ~(kPageSize - 1);
+ return (size + kPageSize - 1) & mask;
+}
-class DiscardableMemoryAllocator::DiscardableAshmemChunk
- : public DiscardableMemory {
- public:
- // Note that |ashmem_region| must outlive |this|.
- DiscardableAshmemChunk(AshmemRegion* ashmem_region,
- int fd,
- void* address,
- size_t offset,
- size_t size)
- : ashmem_region_(ashmem_region),
- fd_(fd),
- address_(address),
- offset_(offset),
- size_(size),
- locked_(true) {
+bool CreateAshmemRegion(const char* name,
+ size_t size,
+ int* out_fd,
+ void** out_address) {
+ base::ScopedFD fd(ashmem_create_region(name, size));
+ if (!fd.is_valid()) {
+ DLOG(ERROR) << "ashmem_create_region() failed";
+ return false;
}
- // Implemented below AshmemRegion since this requires the full definition of
- // AshmemRegion.
- virtual ~DiscardableAshmemChunk();
-
- // DiscardableMemory:
- virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
- DCHECK(!locked_);
- locked_ = true;
- return internal::LockAshmemRegion(fd_, offset_, size_, address_);
+ const int err = ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE);
+ if (err < 0) {
+ DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
+ return false;
}
- virtual void Unlock() OVERRIDE {
- DCHECK(locked_);
- locked_ = false;
- internal::UnlockAshmemRegion(fd_, offset_, size_, address_);
+ // There is a problem using MAP_PRIVATE here. As we are constantly calling
+ // Lock() and Unlock(), data could get lost if they are not written to the
+ // underlying file when Unlock() gets called.
+ void* const address = mmap(
+ NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0);
+ if (address == MAP_FAILED) {
+ DPLOG(ERROR) << "Failed to map memory.";
+ return false;
}
- virtual void* Memory() const OVERRIDE {
- return address_;
+ *out_fd = fd.release();
+ *out_address = address;
+ return true;
+}
+
+bool CloseAshmemRegion(int fd, size_t size, void* address) {
+ if (munmap(address, size) == -1) {
+ DPLOG(ERROR) << "Failed to unmap memory.";
+ close(fd);
+ return false;
}
+ return close(fd) == 0;
+}
- private:
- AshmemRegion* const ashmem_region_;
- const int fd_;
- void* const address_;
- const size_t offset_;
- const size_t size_;
- bool locked_;
+bool LockAshmemRegion(int fd, size_t off, size_t size) {
+ return ashmem_pin_region(fd, off, size) != ASHMEM_WAS_PURGED;
+}
- DISALLOW_COPY_AND_ASSIGN(DiscardableAshmemChunk);
-};
+bool UnlockAshmemRegion(int fd, size_t off, size_t size) {
+ const int failed = ashmem_unpin_region(fd, off, size);
+ if (failed)
+ DLOG(ERROR) << "Failed to unpin memory.";
+ return !failed;
+}
+
+} // namespace
+
+namespace internal {
-class DiscardableMemoryAllocator::AshmemRegion {
+class AshmemRegion {
public:
// Note that |allocator| must outlive |this|.
static scoped_ptr<AshmemRegion> Create(
size_t size,
const std::string& name,
- DiscardableMemoryAllocator* allocator) {
+ DiscardableMemoryAshmemAllocator* allocator) {
+ DCHECK_EQ(size, AlignToNextPage(size));
int fd;
void* base;
- if (!internal::CreateAshmemRegion(name.c_str(), size, &fd, &base))
+ if (!CreateAshmemRegion(name.c_str(), size, &fd, &base))
return scoped_ptr<AshmemRegion>();
return make_scoped_ptr(new AshmemRegion(fd, size, base, allocator));
}
- virtual ~AshmemRegion() {
- const bool result = internal::CloseAshmemRegion(fd_, size_, base_);
+ ~AshmemRegion() {
+ const bool result = CloseAshmemRegion(fd_, size_, base_);
DCHECK(result);
+ DCHECK(!highest_allocated_chunk_);
}
- // Returns a new instance of DiscardableMemory whose size is greater or equal
- // than |actual_size| (which is expected to be greater or equal than
+ // Returns a new instance of DiscardableAshmemChunk whose size is greater or
+ // equal than |actual_size| (which is expected to be greater or equal than
// |client_requested_size|).
// Allocation works as follows:
// 1) Reuse a previously freed chunk and return it if it succeeded. See
@@ -127,21 +144,34 @@ class DiscardableMemoryAllocator::AshmemRegion {
// 3) If there is enough room in the ashmem region then a new chunk is
// returned. This new chunk starts at |offset_| which is the end of the
// previously highest chunk in the region.
- scoped_ptr<DiscardableMemory> Allocate_Locked(size_t client_requested_size,
- size_t actual_size) {
+ scoped_ptr<DiscardableAshmemChunk> Allocate_Locked(
+ size_t client_requested_size,
+ size_t actual_size) {
DCHECK_LE(client_requested_size, actual_size);
allocator_->lock_.AssertAcquired();
- scoped_ptr<DiscardableMemory> memory = ReuseFreeChunk_Locked(
+
+ // Check that the |highest_allocated_chunk_| field doesn't contain a stale
+ // pointer. It should point to either a free chunk or a used chunk.
+ DCHECK(!highest_allocated_chunk_ ||
+ address_to_free_chunk_map_.find(highest_allocated_chunk_) !=
+ address_to_free_chunk_map_.end() ||
+ used_to_previous_chunk_map_.find(highest_allocated_chunk_) !=
+ used_to_previous_chunk_map_.end());
+
+ scoped_ptr<DiscardableAshmemChunk> memory = ReuseFreeChunk_Locked(
client_requested_size, actual_size);
if (memory)
return memory.Pass();
+
if (size_ - offset_ < actual_size) {
// This region does not have enough space left to hold the requested size.
- return scoped_ptr<DiscardableMemory>();
+ return scoped_ptr<DiscardableAshmemChunk>();
}
+
void* const address = static_cast<char*>(base_) + offset_;
memory.reset(
new DiscardableAshmemChunk(this, fd_, address, offset_, actual_size));
+
used_to_previous_chunk_map_.insert(
std::make_pair(address, highest_allocated_chunk_));
highest_allocated_chunk_ = address;
@@ -158,10 +188,19 @@ class DiscardableMemoryAllocator::AshmemRegion {
private:
struct FreeChunk {
+ FreeChunk() : previous_chunk(NULL), start(NULL), size(0) {}
+
+ explicit FreeChunk(size_t size)
+ : previous_chunk(NULL),
+ start(NULL),
+ size(size) {
+ }
+
FreeChunk(void* previous_chunk, void* start, size_t size)
: previous_chunk(previous_chunk),
start(start),
size(size) {
+ DCHECK_LT(previous_chunk, start);
}
void* const previous_chunk;
@@ -179,7 +218,7 @@ class DiscardableMemoryAllocator::AshmemRegion {
AshmemRegion(int fd,
size_t size,
void* base,
- DiscardableMemoryAllocator* allocator)
+ DiscardableMemoryAshmemAllocator* allocator)
: fd_(fd),
size_(size),
base_(base),
@@ -193,14 +232,14 @@ class DiscardableMemoryAllocator::AshmemRegion {
}
// Tries to reuse a previously freed chunk by doing a closest size match.
- scoped_ptr<DiscardableMemory> ReuseFreeChunk_Locked(
+ scoped_ptr<DiscardableAshmemChunk> ReuseFreeChunk_Locked(
size_t client_requested_size,
size_t actual_size) {
allocator_->lock_.AssertAcquired();
const FreeChunk reused_chunk = RemoveFreeChunkFromIterator_Locked(
- free_chunks_.lower_bound(FreeChunk(NULL, NULL, actual_size)));
+ free_chunks_.lower_bound(FreeChunk(actual_size)));
if (reused_chunk.is_null())
- return scoped_ptr<DiscardableMemory>();
+ return scoped_ptr<DiscardableAshmemChunk>();
used_to_previous_chunk_map_.insert(
std::make_pair(reused_chunk.start, reused_chunk.previous_chunk));
@@ -212,6 +251,7 @@ class DiscardableMemoryAllocator::AshmemRegion {
DCHECK_GE(reused_chunk.size, client_requested_size);
const size_t fragmentation_bytes =
reused_chunk.size - client_requested_size;
+
if (fragmentation_bytes > kMaxChunkFragmentationBytes) {
// Split the free chunk being recycled so that its unused tail doesn't get
// reused (i.e. locked) which would prevent it from being evicted under
@@ -219,6 +259,11 @@ class DiscardableMemoryAllocator::AshmemRegion {
reused_chunk_size = actual_size;
void* const new_chunk_start =
static_cast<char*>(reused_chunk.start) + actual_size;
+ if (reused_chunk.start == highest_allocated_chunk_) {
+ // We also need to update the pointer to the highest allocated chunk in
+ // case we are splitting the highest chunk.
+ highest_allocated_chunk_ = new_chunk_start;
+ }
DCHECK_GT(reused_chunk.size, actual_size);
const size_t new_chunk_size = reused_chunk.size - actual_size;
// Note that merging is not needed here since there can't be contiguous
@@ -226,13 +271,13 @@ class DiscardableMemoryAllocator::AshmemRegion {
AddFreeChunk_Locked(
FreeChunk(reused_chunk.start, new_chunk_start, new_chunk_size));
}
+
const size_t offset =
static_cast<char*>(reused_chunk.start) - static_cast<char*>(base_);
- internal::LockAshmemRegion(
- fd_, offset, reused_chunk_size, reused_chunk.start);
- scoped_ptr<DiscardableMemory> memory(
- new DiscardableAshmemChunk(this, fd_, reused_chunk.start, offset,
- reused_chunk_size));
+ LockAshmemRegion(fd_, offset, reused_chunk_size);
+ scoped_ptr<DiscardableAshmemChunk> memory(
+ new DiscardableAshmemChunk(
+ this, fd_, reused_chunk.start, offset, reused_chunk_size));
return memory.Pass();
}
@@ -259,24 +304,34 @@ class DiscardableMemoryAllocator::AshmemRegion {
DCHECK(previous_chunk_it != used_to_previous_chunk_map_.end());
void* previous_chunk = previous_chunk_it->second;
used_to_previous_chunk_map_.erase(previous_chunk_it);
+
if (previous_chunk) {
const FreeChunk free_chunk = RemoveFreeChunk_Locked(previous_chunk);
if (!free_chunk.is_null()) {
new_free_chunk_size += free_chunk.size;
first_free_chunk = previous_chunk;
+ if (chunk == highest_allocated_chunk_)
+ highest_allocated_chunk_ = previous_chunk;
+
// There should not be more contiguous previous free chunks.
- DCHECK(!address_to_free_chunk_map_.count(free_chunk.previous_chunk));
+ previous_chunk = free_chunk.previous_chunk;
+ DCHECK(!address_to_free_chunk_map_.count(previous_chunk));
}
}
+
// Merge with the next chunk if free and present.
void* next_chunk = static_cast<char*>(chunk) + size;
const FreeChunk next_free_chunk = RemoveFreeChunk_Locked(next_chunk);
if (!next_free_chunk.is_null()) {
new_free_chunk_size += next_free_chunk.size;
+ if (next_free_chunk.start == highest_allocated_chunk_)
+ highest_allocated_chunk_ = first_free_chunk;
+
// Same as above.
DCHECK(!address_to_free_chunk_map_.count(static_cast<char*>(next_chunk) +
next_free_chunk.size));
}
+
const bool whole_ashmem_region_is_free =
used_to_previous_chunk_map_.empty();
if (!whole_ashmem_region_is_free) {
@@ -284,11 +339,14 @@ class DiscardableMemoryAllocator::AshmemRegion {
FreeChunk(previous_chunk, first_free_chunk, new_free_chunk_size));
return;
}
+
// The whole ashmem region is free thus it can be deleted.
DCHECK_EQ(base_, first_free_chunk);
+ DCHECK_EQ(base_, highest_allocated_chunk_);
DCHECK(free_chunks_.empty());
DCHECK(address_to_free_chunk_map_.empty());
DCHECK(used_to_previous_chunk_map_.empty());
+ highest_allocated_chunk_ = NULL;
allocator_->DeleteAshmemRegion_Locked(this); // Deletes |this|.
}
@@ -316,7 +374,7 @@ class DiscardableMemoryAllocator::AshmemRegion {
void*, std::multiset<FreeChunk>::iterator>::iterator it =
address_to_free_chunk_map_.find(chunk_start);
if (it == address_to_free_chunk_map_.end())
- return FreeChunk(NULL, NULL, 0U);
+ return FreeChunk();
return RemoveFreeChunkFromIterator_Locked(it->second);
}
@@ -325,7 +383,7 @@ class DiscardableMemoryAllocator::AshmemRegion {
std::multiset<FreeChunk>::iterator free_chunk_it) {
allocator_->lock_.AssertAcquired();
if (free_chunk_it == free_chunks_.end())
- return FreeChunk(NULL, NULL, 0U);
+ return FreeChunk();
DCHECK(free_chunk_it != free_chunks_.end());
const FreeChunk free_chunk(*free_chunk_it);
address_to_free_chunk_map_.erase(free_chunk_it->start);
@@ -336,7 +394,9 @@ class DiscardableMemoryAllocator::AshmemRegion {
const int fd_;
const size_t size_;
void* const base_;
- DiscardableMemoryAllocator* const allocator_;
+ DiscardableMemoryAshmemAllocator* const allocator_;
+ // Points to the chunk with the highest address in the region. This pointer
+ // needs to be carefully updated when chunks are merged/split.
void* highest_allocated_chunk_;
// Points to the end of |highest_allocated_chunk_|.
size_t offset_;
@@ -358,24 +418,61 @@ class DiscardableMemoryAllocator::AshmemRegion {
DISALLOW_COPY_AND_ASSIGN(AshmemRegion);
};
-DiscardableMemoryAllocator::DiscardableAshmemChunk::~DiscardableAshmemChunk() {
+DiscardableAshmemChunk::~DiscardableAshmemChunk() {
if (locked_)
- internal::UnlockAshmemRegion(fd_, offset_, size_, address_);
+ UnlockAshmemRegion(fd_, offset_, size_);
ashmem_region_->OnChunkDeletion(address_, size_);
}
-DiscardableMemoryAllocator::DiscardableMemoryAllocator(const std::string& name)
- : name_(name) {
+bool DiscardableAshmemChunk::Lock() {
+ DCHECK(!locked_);
+ locked_ = true;
+ return LockAshmemRegion(fd_, offset_, size_);
+}
+
+void DiscardableAshmemChunk::Unlock() {
+ DCHECK(locked_);
+ locked_ = false;
+ UnlockAshmemRegion(fd_, offset_, size_);
+}
+
+void* DiscardableAshmemChunk::Memory() const {
+ return address_;
+}
+
+// Note that |ashmem_region| must outlive |this|.
+DiscardableAshmemChunk::DiscardableAshmemChunk(AshmemRegion* ashmem_region,
+ int fd,
+ void* address,
+ size_t offset,
+ size_t size)
+ : ashmem_region_(ashmem_region),
+ fd_(fd),
+ address_(address),
+ offset_(offset),
+ size_(size),
+ locked_(true) {
+}
+
+DiscardableMemoryAshmemAllocator::DiscardableMemoryAshmemAllocator(
+ const std::string& name,
+ size_t ashmem_region_size)
+ : name_(name),
+ ashmem_region_size_(
+ std::max(kMinAshmemRegionSize, AlignToNextPage(ashmem_region_size))),
+ last_ashmem_region_size_(0) {
+ DCHECK_GE(ashmem_region_size_, kMinAshmemRegionSize);
}
-DiscardableMemoryAllocator::~DiscardableMemoryAllocator() {
- DCHECK(thread_checker_.CalledOnValidThread());
+DiscardableMemoryAshmemAllocator::~DiscardableMemoryAshmemAllocator() {
DCHECK(ashmem_regions_.empty());
}
-scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate(
+scoped_ptr<DiscardableAshmemChunk> DiscardableMemoryAshmemAllocator::Allocate(
size_t size) {
- const size_t aligned_size = internal::AlignToNextPage(size);
+ const size_t aligned_size = AlignToNextPage(size);
+ if (!aligned_size)
+ return scoped_ptr<DiscardableAshmemChunk>();
// TODO(pliard): make this function less naive by e.g. moving the free chunks
// multiset to the allocator itself in order to decrease even more
// fragmentation/speedup allocation. Note that there should not be more than a
@@ -384,24 +481,36 @@ scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate(
DCHECK_LE(ashmem_regions_.size(), 5U);
for (ScopedVector<AshmemRegion>::iterator it = ashmem_regions_.begin();
it != ashmem_regions_.end(); ++it) {
- scoped_ptr<DiscardableMemory> memory(
+ scoped_ptr<DiscardableAshmemChunk> memory(
(*it)->Allocate_Locked(size, aligned_size));
if (memory)
return memory.Pass();
}
- scoped_ptr<AshmemRegion> new_region(
- AshmemRegion::Create(
- std::max(static_cast<size_t>(kMinAshmemRegionSize), aligned_size),
- name_.c_str(), this));
- if (!new_region) {
- // TODO(pliard): consider adding an histogram to see how often this happens.
- return scoped_ptr<DiscardableMemory>();
+ // The creation of the (large) ashmem region might fail if the address space
+ // is too fragmented. In case creation fails the allocator retries by
+ // repetitively dividing the size by 2.
+ const size_t min_region_size = std::max(kMinAshmemRegionSize, aligned_size);
+ for (size_t region_size = std::max(ashmem_region_size_, aligned_size);
+ region_size >= min_region_size;
+ region_size = AlignToNextPage(region_size / 2)) {
+ scoped_ptr<AshmemRegion> new_region(
+ AshmemRegion::Create(region_size, name_.c_str(), this));
+ if (!new_region)
+ continue;
+ last_ashmem_region_size_ = region_size;
+ ashmem_regions_.push_back(new_region.release());
+ return ashmem_regions_.back()->Allocate_Locked(size, aligned_size);
}
- ashmem_regions_.push_back(new_region.release());
- return ashmem_regions_.back()->Allocate_Locked(size, aligned_size);
+ // TODO(pliard): consider adding an histogram to see how often this happens.
+ return scoped_ptr<DiscardableAshmemChunk>();
+}
+
+size_t DiscardableMemoryAshmemAllocator::last_ashmem_region_size() const {
+ AutoLock auto_lock(lock_);
+ return last_ashmem_region_size_;
}
-void DiscardableMemoryAllocator::DeleteAshmemRegion_Locked(
+void DiscardableMemoryAshmemAllocator::DeleteAshmemRegion_Locked(
AshmemRegion* region) {
lock_.AssertAcquired();
// Note that there should not be more than a couple of ashmem region instances
diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator.h b/chromium/base/memory/discardable_memory_ashmem_allocator.h
new file mode 100644
index 00000000000..996dde92496
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_ashmem_allocator.h
@@ -0,0 +1,93 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+namespace internal {
+
+class AshmemRegion;
+
+// Internal class, whose instances are returned to the client of the allocator
+// (e.g. DiscardableMemoryAshmem), that mimicks the DiscardableMemory interface.
+class BASE_EXPORT_PRIVATE DiscardableAshmemChunk {
+ public:
+ ~DiscardableAshmemChunk();
+
+ // Returns whether the memory is still resident.
+ bool Lock();
+
+ void Unlock();
+
+ void* Memory() const;
+
+ private:
+ friend class AshmemRegion;
+
+ DiscardableAshmemChunk(AshmemRegion* ashmem_region,
+ int fd,
+ void* address,
+ size_t offset,
+ size_t size);
+
+ AshmemRegion* const ashmem_region_;
+ const int fd_;
+ void* const address_;
+ const size_t offset_;
+ const size_t size_;
+ bool locked_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableAshmemChunk);
+};
+
+// Ashmem regions are backed by a file (descriptor) therefore they are a limited
+// resource. This allocator minimizes the problem by allocating large ashmem
+// regions internally and returning smaller chunks to the client.
+// Allocated chunks are systematically aligned on a page boundary therefore this
+// allocator should not be used for small allocations.
+class BASE_EXPORT_PRIVATE DiscardableMemoryAshmemAllocator {
+ public:
+ // Note that |name| is only used for debugging/measurement purposes.
+ // |ashmem_region_size| is the size that will be used to create the underlying
+ // ashmem regions and is expected to be greater or equal than 32 MBytes.
+ DiscardableMemoryAshmemAllocator(const std::string& name,
+ size_t ashmem_region_size);
+
+ ~DiscardableMemoryAshmemAllocator();
+
+ // Note that the allocator must outlive the returned DiscardableAshmemChunk
+ // instance.
+ scoped_ptr<DiscardableAshmemChunk> Allocate(size_t size);
+
+ // Returns the size of the last ashmem region which was created. This is used
+ // for testing only.
+ size_t last_ashmem_region_size() const;
+
+ private:
+ friend class AshmemRegion;
+
+ void DeleteAshmemRegion_Locked(AshmemRegion* region);
+
+ const std::string name_;
+ const size_t ashmem_region_size_;
+ mutable Lock lock_;
+ size_t last_ashmem_region_size_;
+ ScopedVector<AshmemRegion> ashmem_regions_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryAshmemAllocator);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_ASHMEM_ALLOCATOR_H_
diff --git a/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc b/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc
new file mode 100644
index 00000000000..e9f63ba3439
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_ashmem_allocator_unittest.cc
@@ -0,0 +1,319 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_ashmem_allocator.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/memory/discardable_memory.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/stringprintf.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace internal {
+
+const char kAllocatorName[] = "allocator-for-testing";
+
+const size_t kAshmemRegionSizeForTesting = 32 * 1024 * 1024;
+const size_t kPageSize = 4096;
+
+const size_t kMaxAllowedAllocationSize =
+ std::numeric_limits<size_t>::max() - kPageSize + 1;
+
+class DiscardableMemoryAshmemAllocatorTest : public testing::Test {
+ protected:
+ DiscardableMemoryAshmemAllocatorTest()
+ : allocator_(kAllocatorName, kAshmemRegionSizeForTesting) {
+ }
+
+ DiscardableMemoryAshmemAllocator allocator_;
+};
+
+void WriteToDiscardableAshmemChunk(DiscardableAshmemChunk* memory,
+ size_t size) {
+ // Write to the first and the last pages only to avoid paging in up to 64
+ // MBytes.
+ static_cast<char*>(memory->Memory())[0] = 'a';
+ static_cast<char*>(memory->Memory())[size - 1] = 'a';
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, Basic) {
+ const size_t size = 128;
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(size));
+ ASSERT_TRUE(memory);
+ WriteToDiscardableAshmemChunk(memory.get(), size);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, ZeroAllocationIsNotSupported) {
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(0));
+ ASSERT_FALSE(memory);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, TooLargeAllocationFails) {
+ scoped_ptr<DiscardableAshmemChunk> memory(
+ allocator_.Allocate(kMaxAllowedAllocationSize + 1));
+ // Page-alignment would have caused an overflow resulting in a small
+ // allocation if the input size wasn't checked correctly.
+ ASSERT_FALSE(memory);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ AshmemRegionsAreNotSmallerThanRequestedSize) {
+ // The creation of the underlying ashmem region is expected to fail since
+ // there should not be enough room in the address space. When ashmem creation
+ // fails, the allocator repetitively retries by dividing the size by 2. This
+ // size should not be smaller than the size the user requested so the
+ // allocation here should just fail (and not succeed with the minimum ashmem
+ // region size).
+ scoped_ptr<DiscardableAshmemChunk> memory(
+ allocator_.Allocate(kMaxAllowedAllocationSize));
+ ASSERT_FALSE(memory);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ AshmemRegionsAreAlwaysPageAligned) {
+ // Use a separate allocator here so that we can override the ashmem region
+ // size.
+ DiscardableMemoryAshmemAllocator allocator(
+ kAllocatorName, kMaxAllowedAllocationSize);
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator.Allocate(kPageSize));
+ ASSERT_TRUE(memory);
+ EXPECT_GT(kMaxAllowedAllocationSize, allocator.last_ashmem_region_size());
+ ASSERT_TRUE(allocator.last_ashmem_region_size() % kPageSize == 0);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, LargeAllocation) {
+ const size_t size = 64 * 1024 * 1024;
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(size));
+ ASSERT_TRUE(memory);
+ WriteToDiscardableAshmemChunk(memory.get(), size);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, ChunksArePageAligned) {
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory);
+ EXPECT_EQ(0U, reinterpret_cast<uint64_t>(memory->Memory()) % kPageSize);
+ WriteToDiscardableAshmemChunk(memory.get(), kPageSize);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, AllocateFreeAllocate) {
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize));
+ // Extra allocation that prevents the region from being deleted when |memory|
+ // gets deleted.
+ scoped_ptr<DiscardableAshmemChunk> memory_lock(
+ allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory);
+ void* const address = memory->Memory();
+ memory->Unlock(); // Tests that the reused chunk is being locked correctly.
+ memory.reset();
+ memory = allocator_.Allocate(kPageSize);
+ ASSERT_TRUE(memory);
+ // The previously freed chunk should be reused.
+ EXPECT_EQ(address, memory->Memory());
+ WriteToDiscardableAshmemChunk(memory.get(), kPageSize);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ FreeingWholeAshmemRegionClosesAshmem) {
+ scoped_ptr<DiscardableAshmemChunk> memory(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory);
+ const int kMagic = 0xdeadbeef;
+ *static_cast<int*>(memory->Memory()) = kMagic;
+ memory.reset();
+ // The previous ashmem region should have been closed thus it should not be
+ // reused.
+ memory = allocator_.Allocate(kPageSize);
+ ASSERT_TRUE(memory);
+ EXPECT_NE(kMagic, *static_cast<const int*>(memory->Memory()));
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, AllocateUsesBestFitAlgorithm) {
+ scoped_ptr<DiscardableAshmemChunk> memory1(
+ allocator_.Allocate(3 * kPageSize));
+ ASSERT_TRUE(memory1);
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(2 * kPageSize));
+ ASSERT_TRUE(memory2);
+ scoped_ptr<DiscardableAshmemChunk> memory3(
+ allocator_.Allocate(1 * kPageSize));
+ ASSERT_TRUE(memory3);
+ void* const address_3 = memory3->Memory();
+ memory1.reset();
+ // Don't free |memory2| to avoid merging the 3 blocks together.
+ memory3.reset();
+ memory1 = allocator_.Allocate(1 * kPageSize);
+ ASSERT_TRUE(memory1);
+ // The chunk whose size is closest to the requested size should be reused.
+ EXPECT_EQ(address_3, memory1->Memory());
+ WriteToDiscardableAshmemChunk(memory1.get(), kPageSize);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunks) {
+ scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory1);
+ scoped_ptr<DiscardableAshmemChunk> memory2(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory2);
+ scoped_ptr<DiscardableAshmemChunk> memory3(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory3);
+ scoped_ptr<DiscardableAshmemChunk> memory4(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory4);
+ void* const memory1_address = memory1->Memory();
+ memory1.reset();
+ memory3.reset();
+ // Freeing |memory2| (located between memory1 and memory3) should merge the
+ // three free blocks together.
+ memory2.reset();
+ memory1 = allocator_.Allocate(3 * kPageSize);
+ EXPECT_EQ(memory1_address, memory1->Memory());
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunksAdvanced) {
+ scoped_ptr<DiscardableAshmemChunk> memory1(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory1);
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory2);
+ void* const memory1_address = memory1->Memory();
+ memory1.reset();
+ memory1 = allocator_.Allocate(2 * kPageSize);
+ memory2.reset();
+ // At this point, the region should be in this state:
+ // 8 KBytes (used), 24 KBytes (free).
+ memory2 = allocator_.Allocate(6 * kPageSize);
+ EXPECT_EQ(
+ static_cast<const char*>(memory2->Memory()),
+ static_cast<const char*>(memory1_address) + 2 * kPageSize);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, MergeFreeChunksAdvanced2) {
+ scoped_ptr<DiscardableAshmemChunk> memory1(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory1);
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory2);
+ void* const memory1_address = memory1->Memory();
+ memory1.reset();
+ memory1 = allocator_.Allocate(2 * kPageSize);
+ scoped_ptr<DiscardableAshmemChunk> memory3(
+ allocator_.Allocate(2 * kPageSize));
+ // At this point, the region should be in this state:
+ // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used).
+ memory3.reset();
+ memory2.reset();
+ // At this point, the region should be in this state:
+ // 8 KBytes (used), 24 KBytes (free).
+ memory2 = allocator_.Allocate(6 * kPageSize);
+ EXPECT_EQ(
+ static_cast<const char*>(memory2->Memory()),
+ static_cast<const char*>(memory1_address) + 2 * kPageSize);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ MergeFreeChunksAndDeleteAshmemRegion) {
+ scoped_ptr<DiscardableAshmemChunk> memory1(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory1);
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory2);
+ memory1.reset();
+ memory1 = allocator_.Allocate(2 * kPageSize);
+ scoped_ptr<DiscardableAshmemChunk> memory3(
+ allocator_.Allocate(2 * kPageSize));
+ // At this point, the region should be in this state:
+ // 8 KBytes (used), 8 KBytes (used), 16 KBytes (used).
+ memory1.reset();
+ memory3.reset();
+ // At this point, the region should be in this state:
+ // 8 KBytes (free), 8 KBytes (used), 8 KBytes (free).
+ const int kMagic = 0xdeadbeef;
+ *static_cast<int*>(memory2->Memory()) = kMagic;
+ memory2.reset();
+ // The whole region should have been deleted.
+ memory2 = allocator_.Allocate(2 * kPageSize);
+ EXPECT_NE(kMagic, *static_cast<int*>(memory2->Memory()));
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ TooLargeFreeChunksDontCauseTooMuchFragmentationWhenRecycled) {
+ // Keep |memory_1| below allocated so that the ashmem region doesn't get
+ // closed when |memory_2| is deleted.
+ scoped_ptr<DiscardableAshmemChunk> memory_1(allocator_.Allocate(64 * 1024));
+ ASSERT_TRUE(memory_1);
+ scoped_ptr<DiscardableAshmemChunk> memory_2(allocator_.Allocate(32 * 1024));
+ ASSERT_TRUE(memory_2);
+ void* const address = memory_2->Memory();
+ memory_2.reset();
+ const size_t size = 16 * 1024;
+ memory_2 = allocator_.Allocate(size);
+ ASSERT_TRUE(memory_2);
+ EXPECT_EQ(address, memory_2->Memory());
+ WriteToDiscardableAshmemChunk(memory_2.get(), size);
+ scoped_ptr<DiscardableAshmemChunk> memory_3(allocator_.Allocate(size));
+ // The unused tail (16 KBytes large) of the previously freed chunk should be
+ // reused.
+ EXPECT_EQ(static_cast<char*>(address) + size, memory_3->Memory());
+ WriteToDiscardableAshmemChunk(memory_3.get(), size);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest, UseMultipleAshmemRegions) {
+ // Leave one page untouched at the end of the ashmem region.
+ const size_t size = kAshmemRegionSizeForTesting - kPageSize;
+ scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(size));
+ ASSERT_TRUE(memory1);
+ WriteToDiscardableAshmemChunk(memory1.get(), size);
+
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(kAshmemRegionSizeForTesting));
+ ASSERT_TRUE(memory2);
+ WriteToDiscardableAshmemChunk(memory2.get(), kAshmemRegionSizeForTesting);
+ // The last page of the first ashmem region should be used for this
+ // allocation.
+ scoped_ptr<DiscardableAshmemChunk> memory3(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory3);
+ WriteToDiscardableAshmemChunk(memory3.get(), kPageSize);
+ EXPECT_EQ(memory3->Memory(), static_cast<char*>(memory1->Memory()) + size);
+}
+
+TEST_F(DiscardableMemoryAshmemAllocatorTest,
+ HighestAllocatedChunkPointerIsUpdatedWhenHighestChunkGetsSplit) {
+ // Prevents the ashmem region from getting closed when |memory2| gets freed.
+ scoped_ptr<DiscardableAshmemChunk> memory1(allocator_.Allocate(kPageSize));
+ ASSERT_TRUE(memory1);
+
+ scoped_ptr<DiscardableAshmemChunk> memory2(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory2);
+
+ memory2.reset();
+ memory2 = allocator_.Allocate(kPageSize);
+ // There should now be a free chunk of size 3 * |kPageSize| starting at offset
+ // 2 * |kPageSize| and the pointer to the highest allocated chunk should have
+ // also been updated to |base_| + 2 * |kPageSize|. This pointer is used to
+ // maintain the container mapping a chunk address to its previous chunk and
+ // this map is in turn used while merging previous contiguous chunks.
+
+ // Allocate more than 3 * |kPageSize| so that the free chunk of size 3 *
+ // |kPageSize| is not reused and |highest_allocated_chunk_| gets used instead.
+ scoped_ptr<DiscardableAshmemChunk> memory3(
+ allocator_.Allocate(4 * kPageSize));
+ ASSERT_TRUE(memory3);
+
+ // Deleting |memory3| (whose size is 4 * |kPageSize|) should result in a merge
+ // with its previous chunk which is the free chunk of size |3 * kPageSize|.
+ memory3.reset();
+ memory3 = allocator_.Allocate((3 + 4) * kPageSize);
+ EXPECT_EQ(memory3->Memory(),
+ static_cast<const char*>(memory2->Memory()) + kPageSize);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_emulated.cc b/chromium/base/memory/discardable_memory_emulated.cc
index ed7c42c750f..340a181834a 100644
--- a/chromium/base/memory/discardable_memory_emulated.cc
+++ b/chromium/base/memory/discardable_memory_emulated.cc
@@ -5,60 +5,103 @@
#include "base/memory/discardable_memory_emulated.h"
#include "base/lazy_instance.h"
-#include "base/memory/discardable_memory_provider.h"
+#include "base/memory/discardable_memory_manager.h"
namespace base {
-
namespace {
-base::LazyInstance<internal::DiscardableMemoryProvider>::Leaky g_provider =
- LAZY_INSTANCE_INITIALIZER;
+// This is admittedly pretty magical.
+const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024;
+const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024;
+const size_t kEmulatedBytesToKeepUnderModeratePressure = 4 * 1024 * 1024;
+const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000;
+
+struct SharedState {
+ SharedState()
+ : manager(kEmulatedMemoryLimit,
+ kEmulatedSoftMemoryLimit,
+ kEmulatedBytesToKeepUnderModeratePressure,
+ TimeDelta::FromMilliseconds(
+ kEmulatedHardMemoryLimitExpirationTimeMs)) {}
+
+ internal::DiscardableMemoryManager manager;
+};
+LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER;
} // namespace
namespace internal {
-DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t size)
- : is_locked_(false) {
- g_provider.Pointer()->Register(this, size);
+DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t bytes)
+ : bytes_(bytes),
+ is_locked_(false) {
+ g_shared_state.Pointer()->manager.Register(this, bytes);
}
DiscardableMemoryEmulated::~DiscardableMemoryEmulated() {
if (is_locked_)
Unlock();
- g_provider.Pointer()->Unregister(this);
+ g_shared_state.Pointer()->manager.Unregister(this);
+}
+
+// static
+void DiscardableMemoryEmulated::RegisterMemoryPressureListeners() {
+ g_shared_state.Pointer()->manager.RegisterMemoryPressureListener();
+}
+
+// static
+void DiscardableMemoryEmulated::UnregisterMemoryPressureListeners() {
+ g_shared_state.Pointer()->manager.UnregisterMemoryPressureListener();
+}
+
+// static
+bool DiscardableMemoryEmulated::ReduceMemoryUsage() {
+ return g_shared_state.Pointer()->manager.ReduceMemoryUsage();
+}
+
+// static
+void DiscardableMemoryEmulated::PurgeForTesting() {
+ g_shared_state.Pointer()->manager.PurgeAll();
}
bool DiscardableMemoryEmulated::Initialize() {
- return Lock() == DISCARDABLE_MEMORY_PURGED;
+ return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
}
-LockDiscardableMemoryStatus DiscardableMemoryEmulated::Lock() {
+DiscardableMemoryLockStatus DiscardableMemoryEmulated::Lock() {
DCHECK(!is_locked_);
bool purged = false;
- memory_ = g_provider.Pointer()->Acquire(this, &purged);
- if (!memory_)
- return DISCARDABLE_MEMORY_FAILED;
+ if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged))
+ return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
is_locked_ = true;
- return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
+ return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
+ : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
}
void DiscardableMemoryEmulated::Unlock() {
DCHECK(is_locked_);
- g_provider.Pointer()->Release(this, memory_.Pass());
+ g_shared_state.Pointer()->manager.ReleaseLock(this);
is_locked_ = false;
}
void* DiscardableMemoryEmulated::Memory() const {
+ DCHECK(is_locked_);
DCHECK(memory_);
return memory_.get();
}
-// static
-void DiscardableMemoryEmulated::PurgeForTesting() {
- g_provider.Pointer()->PurgeAll();
+bool DiscardableMemoryEmulated::AllocateAndAcquireLock() {
+ if (memory_)
+ return true;
+
+ memory_.reset(new uint8[bytes_]);
+ return false;
+}
+
+void DiscardableMemoryEmulated::Purge() {
+ memory_.reset();
}
} // namespace internal
diff --git a/chromium/base/memory/discardable_memory_emulated.h b/chromium/base/memory/discardable_memory_emulated.h
index bd0e834ed06..64e99511b7c 100644
--- a/chromium/base/memory/discardable_memory_emulated.h
+++ b/chromium/base/memory/discardable_memory_emulated.h
@@ -7,25 +7,39 @@
#include "base/memory/discardable_memory.h"
+#include "base/memory/discardable_memory_manager.h"
+
namespace base {
namespace internal {
-class DiscardableMemoryEmulated : public DiscardableMemory {
+class DiscardableMemoryEmulated
+ : public DiscardableMemory,
+ public internal::DiscardableMemoryManagerAllocation {
public:
- explicit DiscardableMemoryEmulated(size_t size);
+ explicit DiscardableMemoryEmulated(size_t bytes);
virtual ~DiscardableMemoryEmulated();
+ static void RegisterMemoryPressureListeners();
+ static void UnregisterMemoryPressureListeners();
+ static bool ReduceMemoryUsage();
+
static void PurgeForTesting();
bool Initialize();
// Overridden from DiscardableMemory:
- virtual LockDiscardableMemoryStatus Lock() OVERRIDE;
+ virtual DiscardableMemoryLockStatus Lock() OVERRIDE;
virtual void Unlock() OVERRIDE;
virtual void* Memory() const OVERRIDE;
+ // Overridden from internal::DiscardableMemoryManagerAllocation:
+ virtual bool AllocateAndAcquireLock() OVERRIDE;
+ virtual void ReleaseLock() OVERRIDE {}
+ virtual void Purge() OVERRIDE;
+
private:
- scoped_ptr<uint8, FreeDeleter> memory_;
+ const size_t bytes_;
+ scoped_ptr<uint8[]> memory_;
bool is_locked_;
DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryEmulated);
diff --git a/chromium/base/memory/discardable_memory_linux.cc b/chromium/base/memory/discardable_memory_linux.cc
index 92e39e5e7d4..b9342e92043 100644
--- a/chromium/base/memory/discardable_memory_linux.cc
+++ b/chromium/base/memory/discardable_memory_linux.cc
@@ -2,29 +2,67 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/discardable_memory.h"
+
+#include "base/logging.h"
#include "base/memory/discardable_memory_emulated.h"
+#include "base/memory/discardable_memory_malloc.h"
namespace base {
// static
-bool DiscardableMemory::SupportedNatively() {
- return false;
+void DiscardableMemory::RegisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners();
+}
+
+// static
+void DiscardableMemory::UnregisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners();
+}
+
+// static
+bool DiscardableMemory::ReduceMemoryUsage() {
+ return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
}
// static
-scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
- size_t size) {
- scoped_ptr<internal::DiscardableMemoryEmulated> memory(
- new internal::DiscardableMemoryEmulated(size));
- if (!memory->Initialize())
- return scoped_ptr<DiscardableMemory>();
-
- return memory.PassAs<DiscardableMemory>();
+void DiscardableMemory::GetSupportedTypes(
+ std::vector<DiscardableMemoryType>* types) {
+ const DiscardableMemoryType supported_types[] = {
+ DISCARDABLE_MEMORY_TYPE_EMULATED,
+ DISCARDABLE_MEMORY_TYPE_MALLOC
+ };
+ types->assign(supported_types, supported_types + arraysize(supported_types));
}
// static
-bool DiscardableMemory::PurgeForTestingSupported() {
- return true;
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
+ DiscardableMemoryType type, size_t size) {
+ switch (type) {
+ case DISCARDABLE_MEMORY_TYPE_NONE:
+ case DISCARDABLE_MEMORY_TYPE_ASHMEM:
+ case DISCARDABLE_MEMORY_TYPE_MAC:
+ return scoped_ptr<DiscardableMemory>();
+ case DISCARDABLE_MEMORY_TYPE_EMULATED: {
+ scoped_ptr<internal::DiscardableMemoryEmulated> memory(
+ new internal::DiscardableMemoryEmulated(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_MALLOC: {
+ scoped_ptr<internal::DiscardableMemoryMalloc> memory(
+ new internal::DiscardableMemoryMalloc(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ }
+
+ NOTREACHED();
+ return scoped_ptr<DiscardableMemory>();
}
// static
diff --git a/chromium/base/memory/discardable_memory_mac.cc b/chromium/base/memory/discardable_memory_mac.cc
index aa6823509d5..b2184e7d589 100644
--- a/chromium/base/memory/discardable_memory_mac.cc
+++ b/chromium/base/memory/discardable_memory_mac.cc
@@ -5,69 +5,154 @@
#include "base/memory/discardable_memory.h"
#include <mach/mach.h>
-#include <sys/mman.h>
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_vm.h"
+#include "base/memory/discardable_memory_emulated.h"
+#include "base/memory/discardable_memory_malloc.h"
+#include "base/memory/discardable_memory_manager.h"
#include "base/memory/scoped_ptr.h"
namespace base {
namespace {
+// For Mac, have the DiscardableMemoryManager trigger userspace eviction when
+// address space usage gets too high (e.g. 512 MBytes).
+const size_t kMacMemoryLimit = 512 * 1024 * 1024;
+
+struct SharedState {
+ SharedState()
+ : manager(kMacMemoryLimit,
+ kMacMemoryLimit,
+ kMacMemoryLimit,
+ TimeDelta::Max()) {}
+
+ internal::DiscardableMemoryManager manager;
+};
+LazyInstance<SharedState>::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER;
+
// The VM subsystem allows tagging of memory and 240-255 is reserved for
// application use (see mach/vm_statistics.h). Pick 252 (after chromium's atomic
// weight of ~52).
const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
-class DiscardableMemoryMac : public DiscardableMemory {
+class DiscardableMemoryMac
+ : public DiscardableMemory,
+ public internal::DiscardableMemoryManagerAllocation {
public:
- DiscardableMemoryMac(void* memory, size_t size)
- : memory_(memory),
- size_(size) {
- DCHECK(memory_);
+ explicit DiscardableMemoryMac(size_t bytes)
+ : memory_(0, 0),
+ bytes_(mach_vm_round_page(bytes)),
+ is_locked_(false) {
+ g_shared_state.Pointer()->manager.Register(this, bytes);
}
+ bool Initialize() { return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED; }
+
virtual ~DiscardableMemoryMac() {
- vm_deallocate(mach_task_self(),
- reinterpret_cast<vm_address_t>(memory_),
- size_);
+ if (is_locked_)
+ Unlock();
+ g_shared_state.Pointer()->manager.Unregister(this);
}
- virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
- DCHECK_EQ(0, mprotect(memory_, size_, PROT_READ | PROT_WRITE));
- int state = VM_PURGABLE_NONVOLATILE;
- kern_return_t ret = vm_purgable_control(
- mach_task_self(),
- reinterpret_cast<vm_address_t>(memory_),
- VM_PURGABLE_SET_STATE,
- &state);
- if (ret != KERN_SUCCESS)
- return DISCARDABLE_MEMORY_FAILED;
-
- return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_PURGED
- : DISCARDABLE_MEMORY_SUCCESS;
+ // Overridden from DiscardableMemory:
+ virtual DiscardableMemoryLockStatus Lock() OVERRIDE {
+ DCHECK(!is_locked_);
+
+ bool purged = false;
+ if (!g_shared_state.Pointer()->manager.AcquireLock(this, &purged))
+ return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
+
+ is_locked_ = true;
+ return purged ? DISCARDABLE_MEMORY_LOCK_STATUS_PURGED
+ : DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS;
}
virtual void Unlock() OVERRIDE {
- int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
- kern_return_t ret = vm_purgable_control(
- mach_task_self(),
- reinterpret_cast<vm_address_t>(memory_),
- VM_PURGABLE_SET_STATE,
- &state);
- DCHECK_EQ(0, mprotect(memory_, size_, PROT_NONE));
- if (ret != KERN_SUCCESS)
- DLOG(ERROR) << "Failed to unlock memory.";
+ DCHECK(is_locked_);
+ g_shared_state.Pointer()->manager.ReleaseLock(this);
+ is_locked_ = false;
}
virtual void* Memory() const OVERRIDE {
- return memory_;
+ DCHECK(is_locked_);
+ return reinterpret_cast<void*>(memory_.address());
+ }
+
+ // Overridden from internal::DiscardableMemoryManagerAllocation:
+ virtual bool AllocateAndAcquireLock() OVERRIDE {
+ kern_return_t ret;
+ bool persistent;
+ if (!memory_.size()) {
+ vm_address_t address = 0;
+ ret = vm_allocate(
+ mach_task_self(),
+ &address,
+ bytes_,
+ VM_FLAGS_ANYWHERE | VM_FLAGS_PURGABLE | kDiscardableMemoryTag);
+ MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_allocate";
+ memory_.reset(address, bytes_);
+
+ // When making a fresh allocation, it's impossible for |persistent| to
+ // be true.
+ persistent = false;
+ } else {
+ // |persistent| will be reset to false below if appropriate, but when
+ // reusing an existing allocation, it's possible for it to be true.
+ persistent = true;
+
+#if !defined(NDEBUG)
+ ret = vm_protect(mach_task_self(),
+ memory_.address(),
+ memory_.size(),
+ FALSE,
+ VM_PROT_DEFAULT);
+ MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
+#endif
+ }
+
+ int state = VM_PURGABLE_NONVOLATILE;
+ ret = vm_purgable_control(mach_task_self(),
+ memory_.address(),
+ VM_PURGABLE_SET_STATE,
+ &state);
+ MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
+ if (state & VM_PURGABLE_EMPTY)
+ persistent = false;
+
+ return persistent;
+ }
+
+ virtual void ReleaseLock() OVERRIDE {
+ int state = VM_PURGABLE_VOLATILE | VM_VOLATILE_GROUP_DEFAULT;
+ kern_return_t ret = vm_purgable_control(mach_task_self(),
+ memory_.address(),
+ VM_PURGABLE_SET_STATE,
+ &state);
+ MACH_CHECK(ret == KERN_SUCCESS, ret) << "vm_purgable_control";
+
+#if !defined(NDEBUG)
+ ret = vm_protect(mach_task_self(),
+ memory_.address(),
+ memory_.size(),
+ FALSE,
+ VM_PROT_NONE);
+ MACH_DCHECK(ret == KERN_SUCCESS, ret) << "vm_protect";
+#endif
+ }
+
+ virtual void Purge() OVERRIDE {
+ memory_.reset();
}
private:
- void* const memory_;
- const size_t size_;
+ mac::ScopedMachVM memory_;
+ const size_t bytes_;
+ bool is_locked_;
DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac);
};
@@ -75,37 +160,72 @@ class DiscardableMemoryMac : public DiscardableMemory {
} // namespace
// static
-bool DiscardableMemory::SupportedNatively() {
- return true;
+void DiscardableMemory::RegisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners();
}
// static
-scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
- size_t size) {
- vm_address_t buffer = 0;
- kern_return_t ret = vm_allocate(mach_task_self(),
- &buffer,
- size,
- VM_FLAGS_PURGABLE |
- VM_FLAGS_ANYWHERE |
- kDiscardableMemoryTag);
- if (ret != KERN_SUCCESS) {
- DLOG(ERROR) << "vm_allocate() failed";
- return scoped_ptr<DiscardableMemory>();
- }
- return scoped_ptr<DiscardableMemory>(
- new DiscardableMemoryMac(reinterpret_cast<void*>(buffer), size));
+void DiscardableMemory::UnregisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners();
}
// static
-bool DiscardableMemory::PurgeForTestingSupported() {
- return true;
+bool DiscardableMemory::ReduceMemoryUsage() {
+ return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
+}
+
+// static
+void DiscardableMemory::GetSupportedTypes(
+ std::vector<DiscardableMemoryType>* types) {
+ const DiscardableMemoryType supported_types[] = {
+ DISCARDABLE_MEMORY_TYPE_MAC,
+ DISCARDABLE_MEMORY_TYPE_EMULATED,
+ DISCARDABLE_MEMORY_TYPE_MALLOC
+ };
+ types->assign(supported_types, supported_types + arraysize(supported_types));
+}
+
+// static
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
+ DiscardableMemoryType type, size_t size) {
+ switch (type) {
+ case DISCARDABLE_MEMORY_TYPE_NONE:
+ case DISCARDABLE_MEMORY_TYPE_ASHMEM:
+ return scoped_ptr<DiscardableMemory>();
+ case DISCARDABLE_MEMORY_TYPE_MAC: {
+ scoped_ptr<DiscardableMemoryMac> memory(new DiscardableMemoryMac(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_EMULATED: {
+ scoped_ptr<internal::DiscardableMemoryEmulated> memory(
+ new internal::DiscardableMemoryEmulated(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_MALLOC: {
+ scoped_ptr<internal::DiscardableMemoryMalloc> memory(
+ new internal::DiscardableMemoryMalloc(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ }
+
+ NOTREACHED();
+ return scoped_ptr<DiscardableMemory>();
}
// static
void DiscardableMemory::PurgeForTesting() {
int state = 0;
vm_purgable_control(mach_task_self(), 0, VM_PURGABLE_PURGE_ALL, &state);
+ internal::DiscardableMemoryEmulated::PurgeForTesting();
}
} // namespace base
diff --git a/chromium/base/memory/discardable_memory_malloc.cc b/chromium/base/memory/discardable_memory_malloc.cc
new file mode 100644
index 00000000000..a72f9112d39
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_malloc.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_malloc.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+DiscardableMemoryMalloc::DiscardableMemoryMalloc(size_t size) : size_(size) {
+}
+
+DiscardableMemoryMalloc::~DiscardableMemoryMalloc() {
+}
+
+bool DiscardableMemoryMalloc::Initialize() {
+ return Lock() != DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
+}
+
+DiscardableMemoryLockStatus DiscardableMemoryMalloc::Lock() {
+ DCHECK(!memory_);
+
+ memory_.reset(static_cast<uint8*>(malloc(size_)));
+ if (!memory_)
+ return DISCARDABLE_MEMORY_LOCK_STATUS_FAILED;
+
+ return DISCARDABLE_MEMORY_LOCK_STATUS_PURGED;
+}
+
+void DiscardableMemoryMalloc::Unlock() {
+ DCHECK(memory_);
+ memory_.reset();
+}
+
+void* DiscardableMemoryMalloc::Memory() const {
+ DCHECK(memory_);
+ return memory_.get();
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_malloc.h b/chromium/base/memory/discardable_memory_malloc.h
new file mode 100644
index 00000000000..7729ce5c49b
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_malloc.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_MALLOC_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_MALLOC_H_
+
+#include "base/memory/discardable_memory.h"
+
+namespace base {
+namespace internal {
+
+class DiscardableMemoryMalloc : public DiscardableMemory {
+ public:
+ explicit DiscardableMemoryMalloc(size_t size);
+ virtual ~DiscardableMemoryMalloc();
+
+ bool Initialize();
+
+ // Overridden from DiscardableMemory:
+ virtual DiscardableMemoryLockStatus Lock() OVERRIDE;
+ virtual void Unlock() OVERRIDE;
+ virtual void* Memory() const OVERRIDE;
+
+ private:
+ scoped_ptr<uint8, FreeDeleter> memory_;
+ const size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMalloc);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_MALLOC_H_
diff --git a/chromium/base/memory/discardable_memory_manager.cc b/chromium/base/memory/discardable_memory_manager.cc
new file mode 100644
index 00000000000..d976da203c4
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_manager.cc
@@ -0,0 +1,263 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_manager.h"
+
+#include "base/bind.h"
+#include "base/containers/hash_tables.h"
+#include "base/containers/mru_cache.h"
+#include "base/debug/crash_logging.h"
+#include "base/debug/trace_event.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/synchronization/lock.h"
+
+namespace base {
+namespace internal {
+
+DiscardableMemoryManager::DiscardableMemoryManager(
+ size_t memory_limit,
+ size_t soft_memory_limit,
+ size_t bytes_to_keep_under_moderate_pressure,
+ TimeDelta hard_memory_limit_expiration_time)
+ : allocations_(AllocationMap::NO_AUTO_EVICT),
+ bytes_allocated_(0u),
+ memory_limit_(memory_limit),
+ soft_memory_limit_(soft_memory_limit),
+ bytes_to_keep_under_moderate_pressure_(
+ bytes_to_keep_under_moderate_pressure),
+ hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) {
+ BytesAllocatedChanged(bytes_allocated_);
+}
+
+DiscardableMemoryManager::~DiscardableMemoryManager() {
+ DCHECK(allocations_.empty());
+ DCHECK_EQ(0u, bytes_allocated_);
+}
+
+void DiscardableMemoryManager::RegisterMemoryPressureListener() {
+ AutoLock lock(lock_);
+ DCHECK(base::MessageLoop::current());
+ DCHECK(!memory_pressure_listener_);
+ memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind(
+ &DiscardableMemoryManager::OnMemoryPressure, Unretained(this))));
+}
+
+void DiscardableMemoryManager::UnregisterMemoryPressureListener() {
+ AutoLock lock(lock_);
+ DCHECK(memory_pressure_listener_);
+ memory_pressure_listener_.reset();
+}
+
+void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) {
+ AutoLock lock(lock_);
+ memory_limit_ = bytes;
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now(), memory_limit_);
+}
+
+void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) {
+ AutoLock lock(lock_);
+ soft_memory_limit_ = bytes;
+}
+
+void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure(
+ size_t bytes) {
+ AutoLock lock(lock_);
+ bytes_to_keep_under_moderate_pressure_ = bytes;
+}
+
+void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime(
+ TimeDelta hard_memory_limit_expiration_time) {
+ AutoLock lock(lock_);
+ hard_memory_limit_expiration_time_ = hard_memory_limit_expiration_time;
+}
+
+bool DiscardableMemoryManager::ReduceMemoryUsage() {
+ return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
+}
+
+void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) {
+ AutoLock lock(lock_);
+ // A registered memory listener is currently required. This DCHECK can be
+ // moved or removed if we decide that it's useful to relax this condition.
+ // TODO(reveman): Enable this DCHECK when skia and blink are able to
+ // register memory pressure listeners. crbug.com/333907
+ // DCHECK(memory_pressure_listener_);
+ DCHECK(allocations_.Peek(allocation) == allocations_.end());
+ allocations_.Put(allocation, AllocationInfo(bytes));
+}
+
+void DiscardableMemoryManager::Unregister(Allocation* allocation) {
+ AutoLock lock(lock_);
+ AllocationMap::iterator it = allocations_.Peek(allocation);
+ DCHECK(it != allocations_.end());
+ const AllocationInfo& info = it->second;
+
+ if (info.purgable) {
+ size_t bytes_purgable = info.bytes;
+ DCHECK_LE(bytes_purgable, bytes_allocated_);
+ bytes_allocated_ -= bytes_purgable;
+ BytesAllocatedChanged(bytes_allocated_);
+ }
+ allocations_.Erase(it);
+}
+
+bool DiscardableMemoryManager::AcquireLock(Allocation* allocation,
+ bool* purged) {
+ AutoLock lock(lock_);
+ // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
+ // cache.
+ AllocationMap::iterator it = allocations_.Get(allocation);
+ DCHECK(it != allocations_.end());
+ AllocationInfo* info = &it->second;
+
+ if (!info->bytes)
+ return false;
+
+ TimeTicks now = Now();
+ size_t bytes_required = info->purgable ? 0u : info->bytes;
+
+ if (memory_limit_) {
+ size_t limit = 0;
+ if (bytes_required < memory_limit_)
+ limit = memory_limit_ - bytes_required;
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(now,
+ limit);
+ }
+
+ // Check for overflow.
+ if (std::numeric_limits<size_t>::max() - bytes_required < bytes_allocated_)
+ return false;
+
+ *purged = !allocation->AllocateAndAcquireLock();
+ info->purgable = false;
+ info->last_usage = now;
+ if (bytes_required) {
+ bytes_allocated_ += bytes_required;
+ BytesAllocatedChanged(bytes_allocated_);
+ }
+ return true;
+}
+
+void DiscardableMemoryManager::ReleaseLock(Allocation* allocation) {
+ AutoLock lock(lock_);
+ // Note: |allocations_| is an MRU cache, and use of |Get| here updates that
+ // cache.
+ AllocationMap::iterator it = allocations_.Get(allocation);
+ DCHECK(it != allocations_.end());
+ AllocationInfo* info = &it->second;
+
+ TimeTicks now = Now();
+ allocation->ReleaseLock();
+ info->purgable = true;
+ info->last_usage = now;
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ now, memory_limit_);
+}
+
+void DiscardableMemoryManager::PurgeAll() {
+ AutoLock lock(lock_);
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), 0);
+}
+
+bool DiscardableMemoryManager::IsRegisteredForTest(
+ Allocation* allocation) const {
+ AutoLock lock(lock_);
+ AllocationMap::const_iterator it = allocations_.Peek(allocation);
+ return it != allocations_.end();
+}
+
+bool DiscardableMemoryManager::CanBePurgedForTest(
+ Allocation* allocation) const {
+ AutoLock lock(lock_);
+ AllocationMap::const_iterator it = allocations_.Peek(allocation);
+ return it != allocations_.end() && it->second.purgable;
+}
+
+size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const {
+ AutoLock lock(lock_);
+ return bytes_allocated_;
+}
+
+void DiscardableMemoryManager::OnMemoryPressure(
+ MemoryPressureListener::MemoryPressureLevel pressure_level) {
+ switch (pressure_level) {
+ case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
+ PurgeUntilWithinBytesToKeepUnderModeratePressure();
+ return;
+ case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
+ PurgeAll();
+ return;
+ }
+
+ NOTREACHED();
+}
+
+void
+DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() {
+ AutoLock lock(lock_);
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now(), bytes_to_keep_under_moderate_pressure_);
+}
+
+bool DiscardableMemoryManager::
+ PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() {
+ AutoLock lock(lock_);
+
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ Now() - hard_memory_limit_expiration_time_, soft_memory_limit_);
+
+ return bytes_allocated_ <= soft_memory_limit_;
+}
+
+void DiscardableMemoryManager::
+ PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ TimeTicks timestamp,
+ size_t limit) {
+ lock_.AssertAcquired();
+
+ size_t bytes_allocated_before_purging = bytes_allocated_;
+ for (AllocationMap::reverse_iterator it = allocations_.rbegin();
+ it != allocations_.rend();
+ ++it) {
+ Allocation* allocation = it->first;
+ AllocationInfo* info = &it->second;
+
+ if (bytes_allocated_ <= limit)
+ break;
+
+ bool purgable = info->purgable && info->last_usage <= timestamp;
+ if (!purgable)
+ continue;
+
+ size_t bytes_purgable = info->bytes;
+ DCHECK_LE(bytes_purgable, bytes_allocated_);
+ bytes_allocated_ -= bytes_purgable;
+ info->purgable = false;
+ allocation->Purge();
+ }
+
+ if (bytes_allocated_ != bytes_allocated_before_purging)
+ BytesAllocatedChanged(bytes_allocated_);
+}
+
+void DiscardableMemoryManager::BytesAllocatedChanged(
+ size_t new_bytes_allocated) const {
+ TRACE_COUNTER_ID1(
+ "base", "DiscardableMemoryUsage", this, new_bytes_allocated);
+
+ static const char kDiscardableMemoryUsageKey[] = "dm-usage";
+ base::debug::SetCrashKeyValue(kDiscardableMemoryUsageKey,
+ Uint64ToString(new_bytes_allocated));
+}
+
+TimeTicks DiscardableMemoryManager::Now() const {
+ return TimeTicks::Now();
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_manager.h b/chromium/base/memory/discardable_memory_manager.h
new file mode 100644
index 00000000000..a61f141c430
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_manager.h
@@ -0,0 +1,206 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_
+
+#include "base/base_export.h"
+#include "base/containers/hash_tables.h"
+#include "base/containers/mru_cache.h"
+#include "base/memory/memory_pressure_listener.h"
+#include "base/synchronization/lock.h"
+#include "base/time/time.h"
+
+namespace base {
+namespace internal {
+
+// This interface is used by the DiscardableMemoryManager class to provide some
+// level of userspace control over discardable memory allocations.
+class DiscardableMemoryManagerAllocation {
+ public:
+ // Allocate and acquire a lock that prevents the allocation from being purged
+ // by the system. Returns true if memory was previously allocated and is still
+ // resident.
+ virtual bool AllocateAndAcquireLock() = 0;
+
+ // Release a previously acquired lock on the allocation so that it can be
+ // purged by the system.
+ virtual void ReleaseLock() = 0;
+
+ // Explicitly purge this allocation. It is illegal to call this while a lock
+ // is acquired on the allocation.
+ virtual void Purge() = 0;
+
+ protected:
+ virtual ~DiscardableMemoryManagerAllocation() {}
+};
+
+} // namespace internal
+} // namespace base
+
+#if defined(COMPILER_GCC)
+namespace BASE_HASH_NAMESPACE {
+template <>
+struct hash<base::internal::DiscardableMemoryManagerAllocation*> {
+ size_t operator()(
+ base::internal::DiscardableMemoryManagerAllocation* ptr) const {
+ return hash<size_t>()(reinterpret_cast<size_t>(ptr));
+ }
+};
+} // namespace BASE_HASH_NAMESPACE
+#endif // COMPILER
+
+namespace base {
+namespace internal {
+
+// The DiscardableMemoryManager manages a collection of
+// DiscardableMemoryManagerAllocation instances. It is used on platforms that
+// need some level of userspace control over discardable memory. It keeps track
+// of all allocation instances (in case they need to be purged), and the total
+// amount of allocated memory (in case this forces a purge). When memory usage
+// reaches the limit, the manager purges the LRU memory.
+//
+// When notified of memory pressure, the manager either purges the LRU memory --
+// if the pressure is moderate -- or all discardable memory if the pressure is
+// critical.
+class BASE_EXPORT_PRIVATE DiscardableMemoryManager {
+ public:
+ typedef DiscardableMemoryManagerAllocation Allocation;
+
+ DiscardableMemoryManager(size_t memory_limit,
+ size_t soft_memory_limit,
+ size_t bytes_to_keep_under_moderate_pressure,
+ TimeDelta hard_memory_limit_expiration_time);
+ virtual ~DiscardableMemoryManager();
+
+ // Call this to register memory pressure listener. Must be called on a thread
+ // with a MessageLoop current.
+ void RegisterMemoryPressureListener();
+
+ // Call this to unregister memory pressure listener.
+ void UnregisterMemoryPressureListener();
+
+ // The maximum number of bytes of memory that may be allocated before we force
+ // a purge.
+ void SetMemoryLimit(size_t bytes);
+
+ // The number of bytes of memory that may be allocated but unused for the hard
+ // limit expiration time without getting purged.
+ void SetSoftMemoryLimit(size_t bytes);
+
+ // Sets the amount of memory to keep when we're under moderate pressure.
+ void SetBytesToKeepUnderModeratePressure(size_t bytes);
+
+ // Sets the memory usage cutoff time for hard memory limit.
+ void SetHardMemoryLimitExpirationTime(
+ TimeDelta hard_memory_limit_expiration_time);
+
+ // This will attempt to reduce memory footprint until within soft memory
+ // limit. Returns true if there's no need to call this again until allocations
+ // have been used.
+ bool ReduceMemoryUsage();
+
+ // Adds the given allocation to the manager's collection.
+ void Register(Allocation* allocation, size_t bytes);
+
+ // Removes the given allocation from the manager's collection.
+ void Unregister(Allocation* allocation);
+
+ // Returns false if an error occurred. Otherwise, returns true and sets
+ // |purged| to indicate whether or not allocation has been purged since last
+ // use.
+ bool AcquireLock(Allocation* allocation, bool* purged);
+
+ // Release a previously acquired lock on allocation. This allows the manager
+ // to purge it if necessary.
+ void ReleaseLock(Allocation* allocation);
+
+ // Purges all discardable memory.
+ void PurgeAll();
+
+ // Returns true if allocation has been added to the manager's collection. This
+ // should only be used by tests.
+ bool IsRegisteredForTest(Allocation* allocation) const;
+
+ // Returns true if allocation can be purged. This should only be used by
+ // tests.
+ bool CanBePurgedForTest(Allocation* allocation) const;
+
+ // Returns total amount of allocated discardable memory. This should only be
+ // used by tests.
+ size_t GetBytesAllocatedForTest() const;
+
+ private:
+ struct AllocationInfo {
+ explicit AllocationInfo(size_t bytes) : bytes(bytes), purgable(false) {}
+
+ const size_t bytes;
+ bool purgable;
+ TimeTicks last_usage;
+ };
+ typedef HashingMRUCache<Allocation*, AllocationInfo> AllocationMap;
+
+ // This can be called as a hint that the system is under memory pressure.
+ void OnMemoryPressure(
+ MemoryPressureListener::MemoryPressureLevel pressure_level);
+
+ // Purges memory until usage is less or equal to
+ // |bytes_to_keep_under_moderate_pressure_|.
+ void PurgeUntilWithinBytesToKeepUnderModeratePressure();
+
+ // Purges memory not used since |hard_memory_limit_expiration_time_| before
+ // "right now" until usage is less or equal to |soft_memory_limit_|.
+ // Returns true if total amount of memory is less or equal to soft memory
+ // limit.
+ bool PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit();
+
+ // Purges memory that has not been used since |timestamp| until usage is less
+ // or equal to |limit|.
+ // Caller must acquire |lock_| prior to calling this function.
+ void PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(
+ TimeTicks timestamp,
+ size_t limit);
+
+ // Called when a change to |bytes_allocated_| has been made.
+ void BytesAllocatedChanged(size_t new_bytes_allocated) const;
+
+ // Virtual for tests.
+ virtual TimeTicks Now() const;
+
+ // Needs to be held when accessing members.
+ mutable Lock lock_;
+
+ // A MRU cache of all allocated bits of memory. Used for purging.
+ AllocationMap allocations_;
+
+ // The total amount of allocated memory.
+ size_t bytes_allocated_;
+
+ // The maximum number of bytes of memory that may be allocated.
+ size_t memory_limit_;
+
+ // The number of bytes of memory that may be allocated but not used for
+ // |hard_memory_limit_expiration_time_| amount of time when receiving an idle
+ // notification.
+ size_t soft_memory_limit_;
+
+ // Under moderate memory pressure, we will purge memory until usage is within
+ // this limit.
+ size_t bytes_to_keep_under_moderate_pressure_;
+
+ // Allows us to be respond when the system reports that it is under memory
+ // pressure.
+ scoped_ptr<MemoryPressureListener> memory_pressure_listener_;
+
+ // Amount of time it takes for an allocation to become affected by
+ // |soft_memory_limit_|.
+ TimeDelta hard_memory_limit_expiration_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryManager);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_MANAGER_H_
diff --git a/chromium/base/memory/discardable_memory_manager_unittest.cc b/chromium/base/memory/discardable_memory_manager_unittest.cc
new file mode 100644
index 00000000000..ef5739a6526
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_manager_unittest.cc
@@ -0,0 +1,505 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/memory/discardable_memory_manager.h"
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation {
+ public:
+ TestAllocationImpl() : is_allocated_(false), is_locked_(false) {}
+ virtual ~TestAllocationImpl() { DCHECK(!is_locked_); }
+
+ // Overridden from internal::DiscardableMemoryManagerAllocation:
+ virtual bool AllocateAndAcquireLock() OVERRIDE {
+ bool was_allocated = is_allocated_;
+ is_allocated_ = true;
+ DCHECK(!is_locked_);
+ is_locked_ = true;
+ return was_allocated;
+ }
+ virtual void ReleaseLock() OVERRIDE {
+ DCHECK(is_locked_);
+ is_locked_ = false;
+ }
+ virtual void Purge() OVERRIDE {
+ DCHECK(is_allocated_);
+ is_allocated_ = false;
+ }
+
+ bool is_locked() const { return is_locked_; }
+
+ private:
+ bool is_allocated_;
+ bool is_locked_;
+};
+
+// Tests can assume that the default limit is at least 1024. Tests that rely on
+// something else needs to explicit set the limit.
+const size_t kDefaultMemoryLimit = 1024;
+const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit;
+const size_t kDefaultBytesToKeepUnderModeratePressure = kDefaultMemoryLimit;
+
+class TestDiscardableMemoryManagerImpl
+ : public internal::DiscardableMemoryManager {
+ public:
+ TestDiscardableMemoryManagerImpl()
+ : DiscardableMemoryManager(kDefaultMemoryLimit,
+ kDefaultSoftMemoryLimit,
+ kDefaultBytesToKeepUnderModeratePressure,
+ TimeDelta::Max()) {}
+
+ void SetNow(TimeTicks now) { now_ = now; }
+
+ private:
+ // Overriden from internal::DiscardableMemoryManager:
+ virtual TimeTicks Now() const OVERRIDE { return now_; }
+
+ TimeTicks now_;
+};
+
+class DiscardableMemoryManagerTestBase {
+ public:
+ DiscardableMemoryManagerTestBase() {
+ manager_.RegisterMemoryPressureListener();
+ }
+
+ protected:
+ enum LockStatus {
+ LOCK_STATUS_FAILED,
+ LOCK_STATUS_PURGED,
+ LOCK_STATUS_SUCCESS
+ };
+
+ size_t BytesAllocated() const { return manager_.GetBytesAllocatedForTest(); }
+
+ void SetMemoryLimit(size_t bytes) { manager_.SetMemoryLimit(bytes); }
+
+ void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); }
+
+ void SetBytesToKeepUnderModeratePressure(size_t bytes) {
+ manager_.SetBytesToKeepUnderModeratePressure(bytes);
+ }
+
+ void SetHardMemoryLimitExpirationTime(TimeDelta time) {
+ manager_.SetHardMemoryLimitExpirationTime(time);
+ }
+
+ void Register(TestAllocationImpl* allocation, size_t bytes) {
+ manager_.Register(allocation, bytes);
+ }
+
+ void Unregister(TestAllocationImpl* allocation) {
+ manager_.Unregister(allocation);
+ }
+
+ bool IsRegistered(TestAllocationImpl* allocation) const {
+ return manager_.IsRegisteredForTest(allocation);
+ }
+
+ LockStatus Lock(TestAllocationImpl* allocation) {
+ bool purged;
+ if (!manager_.AcquireLock(allocation, &purged))
+ return LOCK_STATUS_FAILED;
+ return purged ? LOCK_STATUS_PURGED : LOCK_STATUS_SUCCESS;
+ }
+
+ void Unlock(TestAllocationImpl* allocation) {
+ manager_.ReleaseLock(allocation);
+ }
+
+ LockStatus RegisterAndLock(TestAllocationImpl* allocation, size_t bytes) {
+ manager_.Register(allocation, bytes);
+ return Lock(allocation);
+ }
+
+ bool CanBePurged(TestAllocationImpl* allocation) const {
+ return manager_.CanBePurgedForTest(allocation);
+ }
+
+ void SetNow(TimeTicks now) { manager_.SetNow(now); }
+
+ bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); }
+
+ private:
+ MessageLoopForIO message_loop_;
+ TestDiscardableMemoryManagerImpl manager_;
+};
+
+class DiscardableMemoryManagerTest : public DiscardableMemoryManagerTestBase,
+ public testing::Test {
+ public:
+ DiscardableMemoryManagerTest() {}
+};
+
+TEST_F(DiscardableMemoryManagerTest, CreateAndLock) {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ Register(&allocation, size);
+ EXPECT_TRUE(IsRegistered(&allocation));
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation));
+ EXPECT_TRUE(allocation.is_locked());
+ EXPECT_EQ(1024u, BytesAllocated());
+ EXPECT_FALSE(CanBePurged(&allocation));
+ Unlock(&allocation);
+ Unregister(&allocation);
+}
+
+TEST_F(DiscardableMemoryManagerTest, CreateZeroSize) {
+ size_t size = 0;
+ TestAllocationImpl allocation;
+ Register(&allocation, size);
+ EXPECT_TRUE(IsRegistered(&allocation));
+ EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&allocation));
+ EXPECT_EQ(0u, BytesAllocated());
+ Unregister(&allocation);
+}
+
+TEST_F(DiscardableMemoryManagerTest, LockAfterUnlock) {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+ EXPECT_FALSE(CanBePurged(&allocation));
+
+ // Now unlock so we can lock later.
+ Unlock(&allocation);
+ EXPECT_TRUE(CanBePurged(&allocation));
+
+ EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(&allocation));
+ EXPECT_FALSE(CanBePurged(&allocation));
+ Unlock(&allocation);
+ Unregister(&allocation);
+}
+
+TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+ EXPECT_FALSE(CanBePurged(&allocation));
+
+ // Now unlock so we can lock later.
+ Unlock(&allocation);
+ EXPECT_TRUE(CanBePurged(&allocation));
+
+ // Force the system to purge.
+ MemoryPressureListener::NotifyMemoryPressure(
+ MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
+
+ // Required because ObserverListThreadSafe notifies via PostTask.
+ RunLoop().RunUntilIdle();
+
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation));
+ EXPECT_FALSE(CanBePurged(&allocation));
+
+ Unlock(&allocation);
+ Unregister(&allocation);
+}
+
+TEST_F(DiscardableMemoryManagerTest, LockAfterPurgeAndCannotReallocate) {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+ EXPECT_FALSE(CanBePurged(&allocation));
+
+ // Now unlock so we can lock later.
+ Unlock(&allocation);
+ EXPECT_TRUE(CanBePurged(&allocation));
+
+ // Set max allowed allocation to 1 byte. This will cause the memory to be
+ // purged.
+ SetMemoryLimit(1);
+
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation));
+ EXPECT_FALSE(CanBePurged(&allocation));
+
+ Unlock(&allocation);
+ Unregister(&allocation);
+}
+
+TEST_F(DiscardableMemoryManagerTest, Overflow) {
+ size_t size = 1024;
+ {
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+
+ size_t massive_size = std::numeric_limits<size_t>::max();
+ TestAllocationImpl massive_allocation;
+ Register(&massive_allocation, massive_size);
+ EXPECT_EQ(LOCK_STATUS_FAILED, Lock(&massive_allocation));
+ EXPECT_EQ(1024u, BytesAllocated());
+
+ Unlock(&allocation);
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&massive_allocation));
+ Unlock(&massive_allocation);
+ Unregister(&massive_allocation);
+ Unregister(&allocation);
+ }
+ EXPECT_EQ(0u, BytesAllocated());
+}
+
+class PermutationTestData {
+ public:
+ PermutationTestData(unsigned d0, unsigned d1, unsigned d2) {
+ ordering_[0] = d0;
+ ordering_[1] = d1;
+ ordering_[2] = d2;
+ }
+
+ const unsigned* ordering() const { return ordering_; }
+
+ private:
+ unsigned ordering_[3];
+};
+
+class DiscardableMemoryManagerPermutationTest
+ : public DiscardableMemoryManagerTestBase,
+ public testing::TestWithParam<PermutationTestData> {
+ public:
+ DiscardableMemoryManagerPermutationTest() {}
+
+ protected:
+ // Use memory in order specified by ordering parameter.
+ void RegisterAndUseAllocations() {
+ for (int i = 0; i < 3; ++i) {
+ RegisterAndLock(&allocation_[i], 1024);
+ Unlock(&allocation_[i]);
+ }
+ for (int i = 0; i < 3; ++i) {
+ int index = GetParam().ordering()[i];
+ EXPECT_NE(LOCK_STATUS_FAILED, Lock(&allocation_[index]));
+ // Leave i == 0 locked.
+ if (i > 0)
+ Unlock(&allocation_[index]);
+ }
+ }
+
+ TestAllocationImpl* allocation(unsigned position) {
+ return &allocation_[GetParam().ordering()[position]];
+ }
+
+ void UnlockAndUnregisterAllocations() {
+ for (int i = 0; i < 3; ++i) {
+ if (allocation_[i].is_locked())
+ Unlock(&allocation_[i]);
+ Unregister(&allocation_[i]);
+ }
+ }
+
+ private:
+ TestAllocationImpl allocation_[3];
+};
+
+// Verify that memory was discarded in the correct order after applying
+// memory pressure.
+TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) {
+ RegisterAndUseAllocations();
+
+ SetBytesToKeepUnderModeratePressure(1024);
+ SetMemoryLimit(2048);
+
+ MemoryPressureListener::NotifyMemoryPressure(
+ MemoryPressureListener::MEMORY_PRESSURE_MODERATE);
+ RunLoop().RunUntilIdle();
+
+ EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2)));
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1)));
+ // 0 should still be locked.
+ EXPECT_TRUE(allocation(0)->is_locked());
+
+ UnlockAndUnregisterAllocations();
+}
+
+// Verify that memory was discarded in the correct order after changing
+// memory limit.
+TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) {
+ RegisterAndUseAllocations();
+
+ SetBytesToKeepUnderModeratePressure(1024);
+ SetMemoryLimit(2048);
+
+ EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2)));
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1)));
+ // 0 should still be locked.
+ EXPECT_TRUE(allocation(0)->is_locked());
+
+ UnlockAndUnregisterAllocations();
+}
+
+// Verify that no more memory than necessary was discarded after changing
+// memory limit.
+TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) {
+ SetBytesToKeepUnderModeratePressure(2048);
+ SetMemoryLimit(4096);
+
+ RegisterAndUseAllocations();
+
+ SetMemoryLimit(2048);
+
+ EXPECT_EQ(LOCK_STATUS_SUCCESS, Lock(allocation(2)));
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1)));
+ // 0 should still be locked.
+ EXPECT_TRUE(allocation(0)->is_locked());
+
+ UnlockAndUnregisterAllocations();
+}
+
+TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) {
+ RegisterAndUseAllocations();
+
+ MemoryPressureListener::NotifyMemoryPressure(
+ MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
+ RunLoop().RunUntilIdle();
+
+ for (int i = 0; i < 3; ++i) {
+ if (i == 0)
+ EXPECT_TRUE(allocation(i)->is_locked());
+ else
+ EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(i)));
+ }
+
+ UnlockAndUnregisterAllocations();
+}
+
+INSTANTIATE_TEST_CASE_P(DiscardableMemoryManagerPermutationTests,
+ DiscardableMemoryManagerPermutationTest,
+ ::testing::Values(PermutationTestData(0, 1, 2),
+ PermutationTestData(0, 2, 1),
+ PermutationTestData(1, 0, 2),
+ PermutationTestData(1, 2, 0),
+ PermutationTestData(2, 0, 1),
+ PermutationTestData(2, 1, 0)));
+
+TEST_F(DiscardableMemoryManagerTest, NormalDestruction) {
+ {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ Register(&allocation, size);
+ Unregister(&allocation);
+ }
+ EXPECT_EQ(0u, BytesAllocated());
+}
+
+TEST_F(DiscardableMemoryManagerTest, DestructionAfterLocked) {
+ {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+ EXPECT_FALSE(CanBePurged(&allocation));
+ Unlock(&allocation);
+ Unregister(&allocation);
+ }
+ EXPECT_EQ(0u, BytesAllocated());
+}
+
+TEST_F(DiscardableMemoryManagerTest, DestructionAfterPurged) {
+ {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ EXPECT_EQ(1024u, BytesAllocated());
+ Unlock(&allocation);
+ EXPECT_TRUE(CanBePurged(&allocation));
+ SetMemoryLimit(0);
+ EXPECT_EQ(0u, BytesAllocated());
+ Unregister(&allocation);
+ }
+ EXPECT_EQ(0u, BytesAllocated());
+}
+
+TEST_F(DiscardableMemoryManagerTest, ReduceMemoryUsage) {
+ SetMemoryLimit(3072);
+ SetSoftMemoryLimit(1024);
+ SetHardMemoryLimitExpirationTime(TimeDelta::FromInternalValue(1));
+
+ size_t size = 1024;
+ TestAllocationImpl allocation[3];
+ RegisterAndLock(&allocation[0], size);
+ RegisterAndLock(&allocation[1], size);
+ RegisterAndLock(&allocation[2], size);
+ EXPECT_EQ(3072u, BytesAllocated());
+
+ // Above soft limit but nothing that can be purged.
+ EXPECT_FALSE(ReduceMemoryUsage());
+
+ SetNow(TimeTicks::FromInternalValue(0));
+ Unlock(&allocation[0]);
+
+ // Above soft limit but still nothing that can be purged as all unlocked
+ // allocations are within the hard limit cutoff time.
+ EXPECT_FALSE(ReduceMemoryUsage());
+
+ SetNow(TimeTicks::FromInternalValue(1));
+ Unlock(&allocation[1]);
+
+ // One unlocked allocation is no longer within the hard limit cutoff time. It
+ // should be purged and ReduceMemoryUsage() should return false as we're not
+ // yet within the soft memory limit.
+ EXPECT_FALSE(ReduceMemoryUsage());
+ EXPECT_EQ(2048u, BytesAllocated());
+
+ // One more unlocked allocation is no longer within the hard limit cutoff
+ // time. It should be purged and ReduceMemoryUsage() should return true as
+ // we're now within the soft memory limit.
+ SetNow(TimeTicks::FromInternalValue(2));
+ EXPECT_TRUE(ReduceMemoryUsage());
+ EXPECT_EQ(1024u, BytesAllocated());
+
+ Unlock(&allocation[2]);
+
+ Unregister(&allocation[0]);
+ Unregister(&allocation[1]);
+ Unregister(&allocation[2]);
+}
+
+class ThreadedDiscardableMemoryManagerTest
+ : public DiscardableMemoryManagerTest {
+ public:
+ ThreadedDiscardableMemoryManagerTest()
+ : memory_usage_thread_("memory_usage_thread"),
+ thread_sync_(true, false) {}
+
+ virtual void SetUp() OVERRIDE { memory_usage_thread_.Start(); }
+
+ virtual void TearDown() OVERRIDE { memory_usage_thread_.Stop(); }
+
+ void UseMemoryHelper() {
+ size_t size = 1024;
+ TestAllocationImpl allocation;
+ RegisterAndLock(&allocation, size);
+ Unlock(&allocation);
+ Unregister(&allocation);
+ }
+
+ void SignalHelper() { thread_sync_.Signal(); }
+
+ Thread memory_usage_thread_;
+ WaitableEvent thread_sync_;
+};
+
+TEST_F(ThreadedDiscardableMemoryManagerTest, UseMemoryOnThread) {
+ memory_usage_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&ThreadedDiscardableMemoryManagerTest::UseMemoryHelper,
+ Unretained(this)));
+ memory_usage_thread_.message_loop()->PostTask(
+ FROM_HERE,
+ Bind(&ThreadedDiscardableMemoryManagerTest::SignalHelper,
+ Unretained(this)));
+ thread_sync_.Wait();
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_provider.cc b/chromium/base/memory/discardable_memory_provider.cc
deleted file mode 100644
index 1c84339eba7..00000000000
--- a/chromium/base/memory/discardable_memory_provider.cc
+++ /dev/null
@@ -1,215 +0,0 @@
-// Copyright 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.
-
-#include "base/memory/discardable_memory_provider.h"
-
-#include "base/bind.h"
-#include "base/containers/hash_tables.h"
-#include "base/containers/mru_cache.h"
-#include "base/debug/trace_event.h"
-#include "base/synchronization/lock.h"
-#include "base/sys_info.h"
-
-namespace base {
-namespace internal {
-
-namespace {
-
-// This is admittedly pretty magical. It's approximately enough memory for two
-// 2560x1600 images.
-static const size_t kDefaultDiscardableMemoryLimit = 32 * 1024 * 1024;
-static const size_t kDefaultBytesToReclaimUnderModeratePressure =
- kDefaultDiscardableMemoryLimit / 2;
-
-} // namespace
-
-DiscardableMemoryProvider::DiscardableMemoryProvider()
- : allocations_(AllocationMap::NO_AUTO_EVICT),
- bytes_allocated_(0),
- discardable_memory_limit_(kDefaultDiscardableMemoryLimit),
- bytes_to_reclaim_under_moderate_pressure_(
- kDefaultBytesToReclaimUnderModeratePressure),
- memory_pressure_listener_(
- base::Bind(&DiscardableMemoryProvider::NotifyMemoryPressure,
- Unretained(this))) {
-}
-
-DiscardableMemoryProvider::~DiscardableMemoryProvider() {
- DCHECK(allocations_.empty());
- DCHECK_EQ(0u, bytes_allocated_);
-}
-
-void DiscardableMemoryProvider::NotifyMemoryPressure(
- MemoryPressureListener::MemoryPressureLevel pressure_level) {
- switch (pressure_level) {
- case MemoryPressureListener::MEMORY_PRESSURE_MODERATE:
- Purge();
- return;
- case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL:
- PurgeAll();
- return;
- }
-
- NOTREACHED();
-}
-
-void DiscardableMemoryProvider::SetDiscardableMemoryLimit(size_t bytes) {
- AutoLock lock(lock_);
- discardable_memory_limit_ = bytes;
- EnforcePolicyWithLockAcquired();
-}
-
-void DiscardableMemoryProvider::SetBytesToReclaimUnderModeratePressure(
- size_t bytes) {
- AutoLock lock(lock_);
- bytes_to_reclaim_under_moderate_pressure_ = bytes;
-}
-
-void DiscardableMemoryProvider::Register(
- const DiscardableMemory* discardable, size_t bytes) {
- AutoLock lock(lock_);
- DCHECK(allocations_.Peek(discardable) == allocations_.end());
- allocations_.Put(discardable, Allocation(bytes));
-}
-
-void DiscardableMemoryProvider::Unregister(
- const DiscardableMemory* discardable) {
- AutoLock lock(lock_);
- AllocationMap::iterator it = allocations_.Peek(discardable);
- if (it == allocations_.end())
- return;
-
- if (it->second.memory) {
- size_t bytes = it->second.bytes;
- DCHECK_LE(bytes, bytes_allocated_);
- bytes_allocated_ -= bytes;
- free(it->second.memory);
- }
- allocations_.Erase(it);
-}
-
-scoped_ptr<uint8, FreeDeleter> DiscardableMemoryProvider::Acquire(
- const DiscardableMemory* discardable,
- bool* purged) {
- AutoLock lock(lock_);
- // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
- // cache.
- AllocationMap::iterator it = allocations_.Get(discardable);
- CHECK(it != allocations_.end());
-
- if (it->second.memory) {
- scoped_ptr<uint8, FreeDeleter> memory(it->second.memory);
- it->second.memory = NULL;
- *purged = false;
- return memory.Pass();
- }
-
- size_t bytes = it->second.bytes;
- if (!bytes)
- return scoped_ptr<uint8, FreeDeleter>();
-
- if (discardable_memory_limit_) {
- size_t limit = 0;
- if (bytes < discardable_memory_limit_)
- limit = discardable_memory_limit_ - bytes;
-
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit);
- }
-
- // Check for overflow.
- if (std::numeric_limits<size_t>::max() - bytes < bytes_allocated_)
- return scoped_ptr<uint8, FreeDeleter>();
-
- scoped_ptr<uint8, FreeDeleter> memory(static_cast<uint8*>(malloc(bytes)));
- if (!memory)
- return scoped_ptr<uint8, FreeDeleter>();
-
- bytes_allocated_ += bytes;
- *purged = true;
- return memory.Pass();
-}
-
-void DiscardableMemoryProvider::Release(
- const DiscardableMemory* discardable,
- scoped_ptr<uint8, FreeDeleter> memory) {
- AutoLock lock(lock_);
- // NB: |allocations_| is an MRU cache, and use of |Get| here updates that
- // cache.
- AllocationMap::iterator it = allocations_.Get(discardable);
- CHECK(it != allocations_.end());
-
- DCHECK(!it->second.memory);
- it->second.memory = memory.release();
-
- EnforcePolicyWithLockAcquired();
-}
-
-void DiscardableMemoryProvider::PurgeAll() {
- AutoLock lock(lock_);
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(0);
-}
-
-bool DiscardableMemoryProvider::IsRegisteredForTest(
- const DiscardableMemory* discardable) const {
- AutoLock lock(lock_);
- AllocationMap::const_iterator it = allocations_.Peek(discardable);
- return it != allocations_.end();
-}
-
-bool DiscardableMemoryProvider::CanBePurgedForTest(
- const DiscardableMemory* discardable) const {
- AutoLock lock(lock_);
- AllocationMap::const_iterator it = allocations_.Peek(discardable);
- return it != allocations_.end() && it->second.memory;
-}
-
-size_t DiscardableMemoryProvider::GetBytesAllocatedForTest() const {
- AutoLock lock(lock_);
- return bytes_allocated_;
-}
-
-void DiscardableMemoryProvider::Purge() {
- AutoLock lock(lock_);
-
- if (bytes_to_reclaim_under_moderate_pressure_ == 0)
- return;
-
- size_t limit = 0;
- if (bytes_to_reclaim_under_moderate_pressure_ < bytes_allocated_)
- limit = bytes_allocated_ - bytes_to_reclaim_under_moderate_pressure_;
-
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(limit);
-}
-
-void DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin(
- size_t limit) {
- TRACE_EVENT1(
- "base",
- "DiscardableMemoryProvider::PurgeLRUWithLockAcquiredUntilUsageIsWithin",
- "limit", limit);
-
- lock_.AssertAcquired();
-
- for (AllocationMap::reverse_iterator it = allocations_.rbegin();
- it != allocations_.rend();
- ++it) {
- if (bytes_allocated_ <= limit)
- break;
- if (!it->second.memory)
- continue;
-
- size_t bytes = it->second.bytes;
- DCHECK_LE(bytes, bytes_allocated_);
- bytes_allocated_ -= bytes;
- free(it->second.memory);
- it->second.memory = NULL;
- }
-}
-
-void DiscardableMemoryProvider::EnforcePolicyWithLockAcquired() {
- PurgeLRUWithLockAcquiredUntilUsageIsWithin(discardable_memory_limit_);
-}
-
-} // namespace internal
-} // namespace base
diff --git a/chromium/base/memory/discardable_memory_provider.h b/chromium/base/memory/discardable_memory_provider.h
deleted file mode 100644
index 6c343c0d929..00000000000
--- a/chromium/base/memory/discardable_memory_provider.h
+++ /dev/null
@@ -1,143 +0,0 @@
-// Copyright 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_MEMORY_DISCARDABLE_MEMORY_PROVIDER_H_
-#define BASE_MEMORY_DISCARDABLE_MEMORY_PROVIDER_H_
-
-#include "base/base_export.h"
-#include "base/containers/hash_tables.h"
-#include "base/containers/mru_cache.h"
-#include "base/memory/memory_pressure_listener.h"
-#include "base/synchronization/lock.h"
-
-namespace base {
-class DiscardableMemory;
-} // namespace base
-
-#if defined(COMPILER_GCC)
-namespace BASE_HASH_NAMESPACE {
-template <>
-struct hash<const base::DiscardableMemory*> {
- size_t operator()(const base::DiscardableMemory* ptr) const {
- return hash<size_t>()(reinterpret_cast<size_t>(ptr));
- }
-};
-} // namespace BASE_HASH_NAMESPACE
-#endif // COMPILER
-
-namespace base {
-namespace internal {
-
-// The DiscardableMemoryProvider manages a collection of emulated
-// DiscardableMemory instances. It is used on platforms that do not support
-// discardable memory natively. It keeps track of all DiscardableMemory
-// instances (in case they need to be purged), and the total amount of
-// allocated memory (in case this forces a purge).
-//
-// When notified of memory pressure, the provider either purges the LRU
-// memory -- if the pressure is moderate -- or all discardable memory
-// if the pressure is critical.
-//
-// NB - this class is an implementation detail. It has been exposed for testing
-// purposes. You should not need to use this class directly.
-class BASE_EXPORT_PRIVATE DiscardableMemoryProvider {
- public:
- DiscardableMemoryProvider();
- ~DiscardableMemoryProvider();
-
- // The maximum number of bytes of discardable memory that may be allocated
- // before we force a purge. If this amount is zero, it is interpreted as
- // having no limit at all.
- void SetDiscardableMemoryLimit(size_t bytes);
-
- // Sets the amount of memory to reclaim when we're under moderate pressure.
- void SetBytesToReclaimUnderModeratePressure(size_t bytes);
-
- // Adds the given discardable memory to the provider's collection.
- void Register(const DiscardableMemory* discardable, size_t bytes);
-
- // Removes the given discardable memory from the provider's collection.
- void Unregister(const DiscardableMemory* discardable);
-
- // Returns NULL if an error occurred. Otherwise, returns the backing buffer
- // and sets |purged| to indicate whether or not the backing buffer has been
- // purged since last use.
- scoped_ptr<uint8, FreeDeleter> Acquire(
- const DiscardableMemory* discardable, bool* purged);
-
- // Release a previously acquired backing buffer. This gives the buffer back
- // to the provider where it can be purged if necessary.
- void Release(const DiscardableMemory* discardable,
- scoped_ptr<uint8, FreeDeleter> memory);
-
- // Purges all discardable memory.
- void PurgeAll();
-
- // Returns true if discardable memory has been added to the provider's
- // collection. This should only be used by tests.
- bool IsRegisteredForTest(const DiscardableMemory* discardable) const;
-
- // Returns true if discardable memory can be purged. This should only
- // be used by tests.
- bool CanBePurgedForTest(const DiscardableMemory* discardable) const;
-
- // Returns total amount of allocated discardable memory. This should only
- // be used by tests.
- size_t GetBytesAllocatedForTest() const;
-
- private:
- struct Allocation {
- explicit Allocation(size_t bytes)
- : bytes(bytes),
- memory(NULL) {
- }
-
- size_t bytes;
- uint8* memory;
- };
- typedef HashingMRUCache<const DiscardableMemory*, Allocation> AllocationMap;
-
- // This can be called as a hint that the system is under memory pressure.
- void NotifyMemoryPressure(
- MemoryPressureListener::MemoryPressureLevel pressure_level);
-
- // Purges |bytes_to_reclaim_under_moderate_pressure_| bytes of
- // discardable memory.
- void Purge();
-
- // Purges least recently used memory until usage is less or equal to |limit|.
- // Caller must acquire |lock_| prior to calling this function.
- void PurgeLRUWithLockAcquiredUntilUsageIsWithin(size_t limit);
-
- // Ensures that we don't allocate beyond our memory limit.
- // Caller must acquire |lock_| prior to calling this function.
- void EnforcePolicyWithLockAcquired();
-
- // Needs to be held when accessing members.
- mutable Lock lock_;
-
- // A MRU cache of all allocated bits of discardable memory. Used for purging.
- AllocationMap allocations_;
-
- // The total amount of allocated discardable memory.
- size_t bytes_allocated_;
-
- // The maximum number of bytes of discardable memory that may be allocated
- // before we assume moderate memory pressure.
- size_t discardable_memory_limit_;
-
- // Under moderate memory pressure, we will purge this amount of memory.
- size_t bytes_to_reclaim_under_moderate_pressure_;
-
- // Allows us to be respond when the system reports that it is under memory
- // pressure.
- MemoryPressureListener memory_pressure_listener_;
-
- DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryProvider);
-};
-
-} // namespace internal
-} // namespace base
-
-#endif // BASE_MEMORY_DISCARDABLE_MEMORY_PROVIDER_H_
diff --git a/chromium/base/memory/discardable_memory_provider_unittest.cc b/chromium/base/memory/discardable_memory_provider_unittest.cc
deleted file mode 100644
index 1559654c789..00000000000
--- a/chromium/base/memory/discardable_memory_provider_unittest.cc
+++ /dev/null
@@ -1,406 +0,0 @@
-// Copyright 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.
-
-#include "base/memory/discardable_memory_provider.h"
-
-#include "base/bind.h"
-#include "base/memory/discardable_memory.h"
-#include "base/run_loop.h"
-#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-
-class DiscardableMemoryProviderTestBase {
- public:
- class TestDiscardableMemory : public DiscardableMemory {
- public:
- TestDiscardableMemory(
- internal::DiscardableMemoryProvider* provider, size_t size)
- : provider_(provider),
- is_locked_(false) {
- provider_->Register(this, size);
- }
-
- virtual ~TestDiscardableMemory() {
- if (is_locked_)
- Unlock();
- provider_->Unregister(this);
- }
-
- // Overridden from DiscardableMemory:
- virtual LockDiscardableMemoryStatus Lock() OVERRIDE {
- DCHECK(!is_locked_);
-
- bool purged = false;
- memory_ = provider_->Acquire(this, &purged);
- if (!memory_)
- return DISCARDABLE_MEMORY_FAILED;
-
- is_locked_ = true;
- return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
- }
- virtual void Unlock() OVERRIDE {
- DCHECK(is_locked_);
- provider_->Release(this, memory_.Pass());
- is_locked_ = false;
- }
- virtual void* Memory() const OVERRIDE {
- DCHECK(memory_);
- return memory_.get();
- }
-
- private:
- internal::DiscardableMemoryProvider* provider_;
- scoped_ptr<uint8, FreeDeleter> memory_;
- bool is_locked_;
-
- DISALLOW_COPY_AND_ASSIGN(TestDiscardableMemory);
- };
-
- DiscardableMemoryProviderTestBase()
- : message_loop_(MessageLoop::TYPE_IO),
- provider_(new internal::DiscardableMemoryProvider) {
- }
-
- protected:
- bool IsRegistered(const DiscardableMemory* discardable) {
- return provider_->IsRegisteredForTest(discardable);
- }
-
- bool CanBePurged(const DiscardableMemory* discardable) {
- return provider_->CanBePurgedForTest(discardable);
- }
-
- size_t BytesAllocated() const {
- return provider_->GetBytesAllocatedForTest();
- }
-
- void* Memory(const DiscardableMemory* discardable) const {
- return discardable->Memory();
- }
-
- void SetDiscardableMemoryLimit(size_t bytes) {
- provider_->SetDiscardableMemoryLimit(bytes);
- }
-
- void SetBytesToReclaimUnderModeratePressure(size_t bytes) {
- provider_->SetBytesToReclaimUnderModeratePressure(bytes);
- }
-
- scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) {
- scoped_ptr<TestDiscardableMemory> memory(
- new TestDiscardableMemory(provider_.get(), size));
- if (memory->Lock() != DISCARDABLE_MEMORY_PURGED)
- return scoped_ptr<DiscardableMemory>();
- return memory.PassAs<DiscardableMemory>();
- }
-
- private:
- MessageLoop message_loop_;
- scoped_ptr<internal::DiscardableMemoryProvider> provider_;
-};
-
-class DiscardableMemoryProviderTest
- : public DiscardableMemoryProviderTestBase,
- public testing::Test {
- public:
- DiscardableMemoryProviderTest() {}
-};
-
-TEST_F(DiscardableMemoryProviderTest, CreateLockedMemory) {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-}
-
-TEST_F(DiscardableMemoryProviderTest, CreateLockedMemoryZeroSize) {
- size_t size = 0;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_FALSE(discardable);
- EXPECT_FALSE(IsRegistered(discardable.get()));
- EXPECT_EQ(0u, BytesAllocated());
-}
-
-TEST_F(DiscardableMemoryProviderTest, LockAfterUnlock) {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-
- // Now unlock so we can lock later.
- discardable->Unlock();
- EXPECT_TRUE(CanBePurged(discardable.get()));
-
- EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable->Lock());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-}
-
-TEST_F(DiscardableMemoryProviderTest, LockAfterPurge) {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-
- // Now unlock so we can lock later.
- discardable->Unlock();
- EXPECT_TRUE(CanBePurged(discardable.get()));
-
- // Force the system to purge.
- MemoryPressureListener::NotifyMemoryPressure(
- MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
-
- // Required because ObserverListThreadSafe notifies via PostTask.
- RunLoop().RunUntilIdle();
-
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable->Lock());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-}
-
-TEST_F(DiscardableMemoryProviderTest, LockAfterPurgeAndCannotReallocate) {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-
- // Now unlock so we can lock later.
- discardable->Unlock();
- EXPECT_TRUE(CanBePurged(discardable.get()));
-
- // Set max allowed allocation to 1 byte. This will make cause the memory
- // to be purged.
- SetDiscardableMemoryLimit(1);
-
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable->Lock());
- EXPECT_FALSE(CanBePurged(discardable.get()));
-}
-
-TEST_F(DiscardableMemoryProviderTest, Overflow) {
- {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
-
- size_t massive_size = std::numeric_limits<size_t>::max();
- const scoped_ptr<DiscardableMemory> massive_discardable(
- CreateLockedMemory(massive_size));
- EXPECT_FALSE(massive_discardable);
- EXPECT_EQ(1024u, BytesAllocated());
- }
- EXPECT_EQ(0u, BytesAllocated());
-}
-
-class PermutationTestData {
- public:
- PermutationTestData(unsigned d0, unsigned d1, unsigned d2) {
- ordering_[0] = d0;
- ordering_[1] = d1;
- ordering_[2] = d2;
- }
-
- const unsigned* ordering() const { return ordering_; }
-
- private:
- unsigned ordering_[3];
-};
-
-class DiscardableMemoryProviderPermutationTest
- : public DiscardableMemoryProviderTestBase,
- public testing::TestWithParam<PermutationTestData> {
- public:
- DiscardableMemoryProviderPermutationTest() {}
-
- protected:
- // Use discardable memory in order specified by ordering parameter.
- void CreateAndUseDiscardableMemory() {
- for (int i = 0; i < 3; ++i) {
- discardables_[i] = CreateLockedMemory(1024);
- EXPECT_TRUE(discardables_[i]);
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardables_[i].get()));
- discardables_[i]->Unlock();
- }
- for (int i = 0; i < 3; ++i) {
- int index = GetParam().ordering()[i];
- EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardables_[index]->Lock());
- // Leave i == 0 locked.
- if (i > 0)
- discardables_[index]->Unlock();
- }
- }
-
- DiscardableMemory* discardable(unsigned position) {
- return discardables_[GetParam().ordering()[position]].get();
- }
-
- private:
- scoped_ptr<DiscardableMemory> discardables_[3];
-};
-
-// Verify that memory was discarded in the correct order after applying
-// memory pressure.
-TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedModeratePressure) {
- CreateAndUseDiscardableMemory();
-
- SetBytesToReclaimUnderModeratePressure(1024);
- MemoryPressureListener::NotifyMemoryPressure(
- MemoryPressureListener::MEMORY_PRESSURE_MODERATE);
- RunLoop().RunUntilIdle();
-
- EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardable(2)->Lock());
- EXPECT_NE(DISCARDABLE_MEMORY_SUCCESS, discardable(1)->Lock());
- // 0 should still be locked.
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
-}
-
-// Verify that memory was discarded in the correct order after changing
-// memory limit.
-TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedExceedLimit) {
- CreateAndUseDiscardableMemory();
-
- SetBytesToReclaimUnderModeratePressure(1024);
- SetDiscardableMemoryLimit(2048);
-
- EXPECT_NE(DISCARDABLE_MEMORY_FAILED, discardable(2)->Lock());
- EXPECT_NE(DISCARDABLE_MEMORY_SUCCESS, discardable(1)->Lock());
- // 0 should still be locked.
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
-}
-
-// Verify that no more memory than necessary was discarded after changing
-// memory limit.
-TEST_P(DiscardableMemoryProviderPermutationTest, LRUDiscardedAmount) {
- SetBytesToReclaimUnderModeratePressure(2048);
- SetDiscardableMemoryLimit(4096);
-
- CreateAndUseDiscardableMemory();
-
- SetDiscardableMemoryLimit(2048);
-
- EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, discardable(2)->Lock());
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable(1)->Lock());
- // 0 should still be locked.
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(0)));
-}
-
-TEST_P(DiscardableMemoryProviderPermutationTest,
- CriticalPressureFreesAllUnlocked) {
- CreateAndUseDiscardableMemory();
-
- MemoryPressureListener::NotifyMemoryPressure(
- MemoryPressureListener::MEMORY_PRESSURE_CRITICAL);
- RunLoop().RunUntilIdle();
-
- for (int i = 0; i < 3; ++i) {
- if (i == 0)
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable(i)));
- else
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, discardable(i)->Lock());
- }
-}
-
-INSTANTIATE_TEST_CASE_P(DiscardableMemoryProviderPermutationTests,
- DiscardableMemoryProviderPermutationTest,
- ::testing::Values(PermutationTestData(0, 1, 2),
- PermutationTestData(0, 2, 1),
- PermutationTestData(1, 0, 2),
- PermutationTestData(1, 2, 0),
- PermutationTestData(2, 0, 1),
- PermutationTestData(2, 1, 0)));
-
-TEST_F(DiscardableMemoryProviderTest, NormalDestruction) {
- {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- }
- EXPECT_EQ(0u, BytesAllocated());
-}
-
-TEST_F(DiscardableMemoryProviderTest, DestructionWhileLocked) {
- {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
- }
- // Should have ignored the "locked" status and freed the discardable memory.
- EXPECT_EQ(0u, BytesAllocated());
-}
-
-#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
-// Death tests are not supported with Android APKs.
-TEST_F(DiscardableMemoryProviderTest, UnlockedMemoryAccessCrashesInDebugMode) {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- EXPECT_EQ(1024u, BytesAllocated());
- EXPECT_FALSE(CanBePurged(discardable.get()));
- discardable->Unlock();
- EXPECT_TRUE(CanBePurged(discardable.get()));
- // We *must* die if we are asked to vend a pointer to unlocked memory.
- EXPECT_DEATH(discardable->Memory(), ".*Check failed.*");
-}
-#endif
-
-class ThreadedDiscardableMemoryProviderTest
- : public DiscardableMemoryProviderTest {
- public:
- ThreadedDiscardableMemoryProviderTest()
- : memory_usage_thread_("memory_usage_thread"),
- thread_sync_(true, false) {
- }
-
- virtual void SetUp() OVERRIDE {
- memory_usage_thread_.Start();
- }
-
- virtual void TearDown() OVERRIDE {
- memory_usage_thread_.Stop();
- }
-
- void UseMemoryHelper() {
- size_t size = 1024;
- const scoped_ptr<DiscardableMemory> discardable(CreateLockedMemory(size));
- EXPECT_TRUE(IsRegistered(discardable.get()));
- EXPECT_NE(static_cast<void*>(NULL), Memory(discardable.get()));
- discardable->Unlock();
- }
-
- void SignalHelper() {
- thread_sync_.Signal();
- }
-
- Thread memory_usage_thread_;
- WaitableEvent thread_sync_;
-};
-
-TEST_F(ThreadedDiscardableMemoryProviderTest, UseMemoryOnThread) {
- memory_usage_thread_.message_loop()->PostTask(
- FROM_HERE,
- Bind(&ThreadedDiscardableMemoryProviderTest::UseMemoryHelper,
- Unretained(this)));
- memory_usage_thread_.message_loop()->PostTask(
- FROM_HERE,
- Bind(&ThreadedDiscardableMemoryProviderTest::SignalHelper,
- Unretained(this)));
- thread_sync_.Wait();
-}
-
-} // namespace base
diff --git a/chromium/base/memory/discardable_memory_unittest.cc b/chromium/base/memory/discardable_memory_unittest.cc
index c9f67b2556f..dc0e2cd2126 100644
--- a/chromium/base/memory/discardable_memory_unittest.cc
+++ b/chromium/base/memory/discardable_memory_unittest.cc
@@ -4,52 +4,81 @@
#include "base/memory/discardable_memory.h"
-#include <limits>
+#include <algorithm>
+#include "base/run_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
+#if defined(OS_ANDROID)
+#include <limits>
+#endif
+
namespace base {
+namespace {
+
+class DiscardableMemoryTest
+ : public testing::TestWithParam<DiscardableMemoryType> {
+ public:
+ DiscardableMemoryTest() : message_loop_(MessageLoop::TYPE_IO) {
+ // Register memory pressure listeners now that we have a message loop.
+ DiscardableMemory::RegisterMemoryPressureListeners();
+ }
+ virtual ~DiscardableMemoryTest() {
+ DiscardableMemory::UnregisterMemoryPressureListeners();
+ }
+
+ protected:
+ scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size) {
+ return DiscardableMemory::CreateLockedMemoryWithType(
+ GetParam(), size).Pass();
+ }
+
+ private:
+ MessageLoop message_loop_;
+};
const size_t kSize = 1024;
-#if defined(OS_ANDROID)
-TEST(DiscardableMemoryTest, TooLargeAllocationFails) {
- const size_t kPageSize = 4096;
- const size_t max_allowed_allocation_size =
- std::numeric_limits<size_t>::max() - kPageSize + 1;
- scoped_ptr<DiscardableMemory> memory(
- DiscardableMemory::CreateLockedMemory(max_allowed_allocation_size + 1));
- // On certain platforms (e.g. Android), page-alignment would have caused an
- // overflow resulting in a small allocation if the input size wasn't checked
- // correctly.
- ASSERT_FALSE(memory);
+TEST_P(DiscardableMemoryTest, IsNamed) {
+ std::string type_name(DiscardableMemory::GetTypeName(GetParam()));
+ EXPECT_NE("unknown", type_name);
+ EXPECT_EQ(GetParam(), DiscardableMemory::GetNamedType(type_name));
}
-#endif
-TEST(DiscardableMemoryTest, SupportedNatively) {
+bool IsNativeType(DiscardableMemoryType type) {
+ return
+ type == DISCARDABLE_MEMORY_TYPE_ASHMEM ||
+ type == DISCARDABLE_MEMORY_TYPE_MAC;
+}
+
+TEST_P(DiscardableMemoryTest, SupportedNatively) {
+ std::vector<DiscardableMemoryType> supported_types;
+ DiscardableMemory::GetSupportedTypes(&supported_types);
#if defined(DISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY)
- ASSERT_TRUE(DiscardableMemory::SupportedNatively());
+ EXPECT_NE(0, std::count_if(supported_types.begin(),
+ supported_types.end(),
+ IsNativeType));
#else
// If we ever have a platform that decides at runtime if it can support
// discardable memory natively, then we'll have to add a 'never supported
// natively' define for this case. At present, if it's not always supported
// natively, it's never supported.
- ASSERT_FALSE(DiscardableMemory::SupportedNatively());
+ EXPECT_EQ(0, std::count_if(supported_types.begin(),
+ supported_types.end(),
+ IsNativeType));
#endif
}
// Test Lock() and Unlock() functionalities.
-TEST(DiscardableMemoryTest, LockAndUnLock) {
- const scoped_ptr<DiscardableMemory> memory(
- DiscardableMemory::CreateLockedMemory(kSize));
+TEST_P(DiscardableMemoryTest, LockAndUnLock) {
+ const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize));
ASSERT_TRUE(memory);
void* addr = memory->Memory();
ASSERT_NE(static_cast<void*>(NULL), addr);
memory->Unlock();
- // The system should have no reason to purge discardable blocks in this brief
- // interval, though technically speaking this might flake.
- EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, memory->Lock());
+
+ EXPECT_NE(DISCARDABLE_MEMORY_LOCK_STATUS_FAILED, memory->Lock());
addr = memory->Memory();
ASSERT_NE(static_cast<void*>(NULL), addr);
@@ -57,32 +86,25 @@ TEST(DiscardableMemoryTest, LockAndUnLock) {
}
// Test delete a discardable memory while it is locked.
-TEST(DiscardableMemoryTest, DeleteWhileLocked) {
- const scoped_ptr<DiscardableMemory> memory(
- DiscardableMemory::CreateLockedMemory(kSize));
+TEST_P(DiscardableMemoryTest, DeleteWhileLocked) {
+ const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize));
ASSERT_TRUE(memory);
}
-#if !defined(OS_ANDROID)
// Test forced purging.
-TEST(DiscardableMemoryTest, Purge) {
- ASSERT_TRUE(DiscardableMemory::PurgeForTestingSupported());
-
- const scoped_ptr<DiscardableMemory> memory(
- DiscardableMemory::CreateLockedMemory(kSize));
+TEST_P(DiscardableMemoryTest, Purge) {
+ const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize));
ASSERT_TRUE(memory);
memory->Unlock();
DiscardableMemory::PurgeForTesting();
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, memory->Lock());
+ EXPECT_EQ(DISCARDABLE_MEMORY_LOCK_STATUS_PURGED, memory->Lock());
}
-#endif // !OS_ANDROID
#if !defined(NDEBUG) && !defined(OS_ANDROID)
// Death tests are not supported with Android APKs.
-TEST(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) {
- const scoped_ptr<DiscardableMemory> memory(
- DiscardableMemory::CreateLockedMemory(kSize));
+TEST_P(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) {
+ const scoped_ptr<DiscardableMemory> memory(CreateLockedMemory(kSize));
ASSERT_TRUE(memory);
memory->Unlock();
ASSERT_DEATH_IF_SUPPORTED(
@@ -90,4 +112,16 @@ TEST(DiscardableMemoryTest, UnlockedMemoryAccessCrashesInDebugMode) {
}
#endif
+std::vector<DiscardableMemoryType> GetSupportedDiscardableMemoryTypes() {
+ std::vector<DiscardableMemoryType> supported_types;
+ DiscardableMemory::GetSupportedTypes(&supported_types);
+ return supported_types;
}
+
+INSTANTIATE_TEST_CASE_P(
+ DiscardableMemoryTests,
+ DiscardableMemoryTest,
+ ::testing::ValuesIn(GetSupportedDiscardableMemoryTypes()));
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_win.cc b/chromium/base/memory/discardable_memory_win.cc
index 92e39e5e7d4..b9342e92043 100644
--- a/chromium/base/memory/discardable_memory_win.cc
+++ b/chromium/base/memory/discardable_memory_win.cc
@@ -2,29 +2,67 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/memory/discardable_memory.h"
+
+#include "base/logging.h"
#include "base/memory/discardable_memory_emulated.h"
+#include "base/memory/discardable_memory_malloc.h"
namespace base {
// static
-bool DiscardableMemory::SupportedNatively() {
- return false;
+void DiscardableMemory::RegisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners();
+}
+
+// static
+void DiscardableMemory::UnregisterMemoryPressureListeners() {
+ internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners();
+}
+
+// static
+bool DiscardableMemory::ReduceMemoryUsage() {
+ return internal::DiscardableMemoryEmulated::ReduceMemoryUsage();
}
// static
-scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
- size_t size) {
- scoped_ptr<internal::DiscardableMemoryEmulated> memory(
- new internal::DiscardableMemoryEmulated(size));
- if (!memory->Initialize())
- return scoped_ptr<DiscardableMemory>();
-
- return memory.PassAs<DiscardableMemory>();
+void DiscardableMemory::GetSupportedTypes(
+ std::vector<DiscardableMemoryType>* types) {
+ const DiscardableMemoryType supported_types[] = {
+ DISCARDABLE_MEMORY_TYPE_EMULATED,
+ DISCARDABLE_MEMORY_TYPE_MALLOC
+ };
+ types->assign(supported_types, supported_types + arraysize(supported_types));
}
// static
-bool DiscardableMemory::PurgeForTestingSupported() {
- return true;
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemoryWithType(
+ DiscardableMemoryType type, size_t size) {
+ switch (type) {
+ case DISCARDABLE_MEMORY_TYPE_NONE:
+ case DISCARDABLE_MEMORY_TYPE_ASHMEM:
+ case DISCARDABLE_MEMORY_TYPE_MAC:
+ return scoped_ptr<DiscardableMemory>();
+ case DISCARDABLE_MEMORY_TYPE_EMULATED: {
+ scoped_ptr<internal::DiscardableMemoryEmulated> memory(
+ new internal::DiscardableMemoryEmulated(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ case DISCARDABLE_MEMORY_TYPE_MALLOC: {
+ scoped_ptr<internal::DiscardableMemoryMalloc> memory(
+ new internal::DiscardableMemoryMalloc(size));
+ if (!memory->Initialize())
+ return scoped_ptr<DiscardableMemory>();
+
+ return memory.PassAs<DiscardableMemory>();
+ }
+ }
+
+ NOTREACHED();
+ return scoped_ptr<DiscardableMemory>();
}
// static
diff --git a/chromium/base/memory/ref_counted.cc b/chromium/base/memory/ref_counted.cc
index 31ad5098cd0..f5924d0fe76 100644
--- a/chromium/base/memory/ref_counted.cc
+++ b/chromium/base/memory/ref_counted.cc
@@ -3,54 +3,12 @@
// found in the LICENSE file.
#include "base/memory/ref_counted.h"
-
-#include "base/logging.h"
#include "base/threading/thread_collision_warner.h"
namespace base {
namespace subtle {
-RefCountedBase::RefCountedBase()
- : ref_count_(0)
-#ifndef NDEBUG
- , in_dtor_(false)
-#endif
- {
-}
-
-RefCountedBase::~RefCountedBase() {
-#ifndef NDEBUG
- DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
-#endif
-}
-
-void RefCountedBase::AddRef() const {
- // TODO(maruel): Add back once it doesn't assert 500 times/sec.
- // Current thread books the critical section "AddRelease" without release it.
- // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
-#ifndef NDEBUG
- DCHECK(!in_dtor_);
-#endif
- ++ref_count_;
-}
-
-bool RefCountedBase::Release() const {
- // TODO(maruel): Add back once it doesn't assert 500 times/sec.
- // Current thread books the critical section "AddRelease" without release it.
- // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
-#ifndef NDEBUG
- DCHECK(!in_dtor_);
-#endif
- if (--ref_count_ == 0) {
-#ifndef NDEBUG
- in_dtor_ = true;
-#endif
- return true;
- }
- return false;
-}
-
bool RefCountedThreadSafeBase::HasOneRef() const {
return AtomicRefCountIsOne(
&const_cast<RefCountedThreadSafeBase*>(this)->ref_count_);
diff --git a/chromium/base/memory/ref_counted.h b/chromium/base/memory/ref_counted.h
index aae36b17c2f..be5eda8e083 100644
--- a/chromium/base/memory/ref_counted.h
+++ b/chromium/base/memory/ref_counted.h
@@ -10,6 +10,9 @@
#include "base/atomic_ref_count.h"
#include "base/base_export.h"
#include "base/compiler_specific.h"
+#ifndef NDEBUG
+#include "base/logging.h"
+#endif
#include "base/threading/thread_collision_warner.h"
namespace base {
@@ -21,13 +24,49 @@ class BASE_EXPORT RefCountedBase {
bool HasOneRef() const { return ref_count_ == 1; }
protected:
- RefCountedBase();
- ~RefCountedBase();
+ RefCountedBase()
+ : ref_count_(0)
+ #ifndef NDEBUG
+ , in_dtor_(false)
+ #endif
+ {
+ }
+
+ ~RefCountedBase() {
+ #ifndef NDEBUG
+ DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+ #endif
+ }
- void AddRef() const;
+
+ void AddRef() const {
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease"
+ // without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+ #ifndef NDEBUG
+ DCHECK(!in_dtor_);
+ #endif
+ ++ref_count_;
+ }
// Returns true if the object should self-delete.
- bool Release() const;
+ bool Release() const {
+ // TODO(maruel): Add back once it doesn't assert 500 times/sec.
+ // Current thread books the critical section "AddRelease"
+ // without release it.
+ // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_);
+ #ifndef NDEBUG
+ DCHECK(!in_dtor_);
+ #endif
+ if (--ref_count_ == 0) {
+ #ifndef NDEBUG
+ in_dtor_ = true;
+ #endif
+ return true;
+ }
+ return false;
+ }
private:
mutable int ref_count_;
diff --git a/chromium/base/memory/ref_counted_memory.cc b/chromium/base/memory/ref_counted_memory.cc
index b1deee11201..477c941355c 100644
--- a/chromium/base/memory/ref_counted_memory.cc
+++ b/chromium/base/memory/ref_counted_memory.cc
@@ -37,6 +37,9 @@ RefCountedBytes::RefCountedBytes(const std::vector<unsigned char>& initializer)
: data_(initializer) {
}
+RefCountedBytes::RefCountedBytes(const unsigned char* p, size_t size)
+ : data_(p, p + size) {}
+
RefCountedBytes* RefCountedBytes::TakeVector(
std::vector<unsigned char>* to_destroy) {
RefCountedBytes* bytes = new RefCountedBytes;
diff --git a/chromium/base/memory/ref_counted_memory.h b/chromium/base/memory/ref_counted_memory.h
index d2987c5d21b..a238c3a30fe 100644
--- a/chromium/base/memory/ref_counted_memory.h
+++ b/chromium/base/memory/ref_counted_memory.h
@@ -30,6 +30,11 @@ class BASE_EXPORT RefCountedMemory
// Returns true if |other| is byte for byte equal.
bool Equals(const scoped_refptr<RefCountedMemory>& other) const;
+ // Handy method to simplify calling front() with a reinterpret_cast.
+ template<typename T> const T* front_as() const {
+ return reinterpret_cast<const T*>(front());
+ }
+
protected:
friend class base::RefCountedThreadSafe<RefCountedMemory>;
RefCountedMemory();
@@ -42,8 +47,9 @@ class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory {
public:
RefCountedStaticMemory()
: data_(NULL), length_(0) {}
- RefCountedStaticMemory(const unsigned char* data, size_t length)
- : data_(length ? data : NULL), length_(length) {}
+ RefCountedStaticMemory(const void* data, size_t length)
+ : data_(static_cast<const unsigned char*>(length ? data : NULL)),
+ length_(length) {}
// Overridden from RefCountedMemory:
virtual const unsigned char* front() const OVERRIDE;
@@ -58,8 +64,7 @@ class BASE_EXPORT RefCountedStaticMemory : public RefCountedMemory {
DISALLOW_COPY_AND_ASSIGN(RefCountedStaticMemory);
};
-// An implementation of RefCountedMemory, where we own our the data in a
-// vector.
+// An implementation of RefCountedMemory, where we own the data in a vector.
class BASE_EXPORT RefCountedBytes : public RefCountedMemory {
public:
RefCountedBytes();
@@ -67,6 +72,9 @@ class BASE_EXPORT RefCountedBytes : public RefCountedMemory {
// Constructs a RefCountedBytes object by _copying_ from |initializer|.
explicit RefCountedBytes(const std::vector<unsigned char>& initializer);
+ // Constructs a RefCountedBytes object by copying |size| bytes from |p|.
+ RefCountedBytes(const unsigned char* p, size_t size);
+
// Constructs a RefCountedBytes object by performing a swap. (To non
// destructively build a RefCountedBytes, use the constructor that takes a
// vector.)
diff --git a/chromium/base/memory/ref_counted_memory_unittest.cc b/chromium/base/memory/ref_counted_memory_unittest.cc
index 88c5b534851..5bfc1c79a69 100644
--- a/chromium/base/memory/ref_counted_memory_unittest.cc
+++ b/chromium/base/memory/ref_counted_memory_unittest.cc
@@ -10,12 +10,10 @@ namespace base {
TEST(RefCountedMemoryUnitTest, RefCountedStaticMemory) {
scoped_refptr<RefCountedMemory> mem = new RefCountedStaticMemory(
- reinterpret_cast<const uint8*>("static mem00"), 10);
+ "static mem00", 10);
EXPECT_EQ(10U, mem->size());
- EXPECT_EQ("static mem",
- std::string(reinterpret_cast<const char*>(mem->front()),
- mem->size()));
+ EXPECT_EQ("static mem", std::string(mem->front_as<char>(), mem->size()));
}
TEST(RefCountedMemoryUnitTest, RefCountedBytes) {
@@ -29,6 +27,16 @@ TEST(RefCountedMemoryUnitTest, RefCountedBytes) {
EXPECT_EQ(2U, mem->size());
EXPECT_EQ(45U, mem->front()[0]);
EXPECT_EQ(99U, mem->front()[1]);
+
+ scoped_refptr<RefCountedMemory> mem2;
+ {
+ unsigned char data2[] = { 12, 11, 99 };
+ mem2 = new RefCountedBytes(data2, 3);
+ }
+ EXPECT_EQ(3U, mem2->size());
+ EXPECT_EQ(12U, mem2->front()[0]);
+ EXPECT_EQ(11U, mem2->front()[1]);
+ EXPECT_EQ(99U, mem2->front()[2]);
}
TEST(RefCountedMemoryUnitTest, RefCountedString) {
diff --git a/chromium/base/memory/scoped_handle.h b/chromium/base/memory/scoped_handle.h
deleted file mode 100644
index b95559dcd83..00000000000
--- a/chromium/base/memory/scoped_handle.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2011 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_MEMORY_SCOPED_HANDLE_H_
-#define BASE_MEMORY_SCOPED_HANDLE_H_
-
-#include <stdio.h>
-
-#include "base/basictypes.h"
-
-class ScopedStdioHandle {
- public:
- ScopedStdioHandle()
- : handle_(NULL) { }
-
- explicit ScopedStdioHandle(FILE* handle)
- : handle_(handle) { }
-
- ~ScopedStdioHandle() {
- Close();
- }
-
- void Close() {
- if (handle_) {
- fclose(handle_);
- handle_ = NULL;
- }
- }
-
- FILE* get() const { return handle_; }
-
- FILE* Take() {
- FILE* temp = handle_;
- handle_ = NULL;
- return temp;
- }
-
- void Set(FILE* newhandle) {
- Close();
- handle_ = newhandle;
- }
-
- private:
- FILE* handle_;
-
- DISALLOW_COPY_AND_ASSIGN(ScopedStdioHandle);
-};
-
-#endif // BASE_MEMORY_SCOPED_HANDLE_H_
diff --git a/chromium/base/memory/scoped_ptr.h b/chromium/base/memory/scoped_ptr.h
index 790fecce71e..bf2e0b6f068 100644
--- a/chromium/base/memory/scoped_ptr.h
+++ b/chromium/base/memory/scoped_ptr.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Scopers help you manage ownership of a pointer, helping you easily manage the
-// a pointer within a scope, and automatically destroying the pointer at the
-// end of a scope. There are two main classes you will use, which correspond
-// to the operators new/delete and new[]/delete[].
+// Scopers help you manage ownership of a pointer, helping you easily manage a
+// pointer within a scope, and automatically destroying the pointer at the end
+// of a scope. There are two main classes you will use, which correspond to the
+// operators new/delete and new[]/delete[].
//
// Example usage (scoped_ptr<T>):
// {
@@ -88,7 +88,7 @@
#define BASE_MEMORY_SCOPED_PTR_H_
// This is an implementation designed to match the anticipated future TR2
-// implementation of the scoped_ptr class and scoped_ptr_malloc (deprecated).
+// implementation of the scoped_ptr class.
#include <assert.h>
#include <stddef.h>
@@ -227,7 +227,7 @@ class scoped_ptr_impl {
abort();
// Note that running data_.ptr = p can lead to undefined behavior if
- // get_deleter()(get()) deletes this. In order to pevent this, reset()
+ // get_deleter()(get()) deletes this. In order to prevent this, reset()
// should update the stored pointer before deleting its old value.
//
// However, changing reset() to use that behavior may cause current code to
@@ -570,134 +570,6 @@ bool operator!=(T* p1, const scoped_ptr<T, D>& p2) {
return p1 != p2.get();
}
-// DEPRECATED: Use scoped_ptr<C, base::FreeDeleter> instead.
-//
-// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
-// second template argument, the functor used to free the object.
-
-template<class C, class FreeProc = base::FreeDeleter>
-class scoped_ptr_malloc {
- MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr_malloc, RValue)
-
- public:
-
- // The element type
- typedef C element_type;
-
- // Constructor. Defaults to initializing with NULL.
- // There is no way to create an uninitialized scoped_ptr.
- // The input parameter must be allocated with an allocator that matches the
- // Free functor. For the default Free functor, this is malloc, calloc, or
- // realloc.
- explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
-
- // Constructor. Move constructor for C++03 move emulation of this type.
- scoped_ptr_malloc(RValue rvalue)
- : ptr_(rvalue.object->release()) {
- }
-
- // Destructor. If there is a C object, call the Free functor.
- ~scoped_ptr_malloc() {
- reset();
- }
-
- // operator=. Move operator= for C++03 move emulation of this type.
- scoped_ptr_malloc& operator=(RValue rhs) {
- reset(rhs.object->release());
- return *this;
- }
-
- // Reset. Calls the Free functor on the current owned object, if any.
- // Then takes ownership of a new object, if given.
- // this->reset(this->get()) works.
- void reset(C* p = NULL) {
- if (ptr_ != p) {
- if (ptr_ != NULL) {
- FreeProc free_proc;
- free_proc(ptr_);
- }
- ptr_ = p;
- }
- }
-
- // Get the current object.
- // operator* and operator-> will cause an assert() failure if there is
- // no current object.
- C& operator*() const {
- assert(ptr_ != NULL);
- return *ptr_;
- }
-
- C* operator->() const {
- assert(ptr_ != NULL);
- return ptr_;
- }
-
- C* get() const {
- return ptr_;
- }
-
- // Allow scoped_ptr_malloc<C> to be used in boolean expressions, but not
- // implicitly convertible to a real bool (which is dangerous).
- typedef C* scoped_ptr_malloc::*Testable;
- operator Testable() const { return ptr_ ? &scoped_ptr_malloc::ptr_ : NULL; }
-
- // Comparison operators.
- // These return whether a scoped_ptr_malloc and a plain pointer refer
- // to the same object, not just to two different but equal objects.
- // For compatibility with the boost-derived implementation, these
- // take non-const arguments.
- bool operator==(C* p) const {
- return ptr_ == p;
- }
-
- bool operator!=(C* p) const {
- return ptr_ != p;
- }
-
- // Swap two scoped pointers.
- void swap(scoped_ptr_malloc & b) {
- C* tmp = b.ptr_;
- b.ptr_ = ptr_;
- ptr_ = tmp;
- }
-
- // Release a pointer.
- // The return value is the current pointer held by this object.
- // If this object holds a NULL pointer, the return value is NULL.
- // After this operation, this object will hold a NULL pointer,
- // and will not own the object any more.
- C* release() WARN_UNUSED_RESULT {
- C* tmp = ptr_;
- ptr_ = NULL;
- return tmp;
- }
-
- private:
- C* ptr_;
-
- // no reason to use these: each scoped_ptr_malloc should have its own object
- template <class C2, class GP>
- bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
- template <class C2, class GP>
- bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
-};
-
-template<class C, class FP> inline
-void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
- a.swap(b);
-}
-
-template<class C, class FP> inline
-bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
- return p == b.get();
-}
-
-template<class C, class FP> inline
-bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
- return p != b.get();
-}
-
// A function to convert T* into scoped_ptr<T>
// Doing e.g. make_scoped_ptr(new FooBarBaz<type>(arg)) is a shorter notation
// for scoped_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
diff --git a/chromium/base/memory/scoped_ptr_unittest.cc b/chromium/base/memory/scoped_ptr_unittest.cc
index 22da53d3511..e0c15484e08 100644
--- a/chromium/base/memory/scoped_ptr_unittest.cc
+++ b/chromium/base/memory/scoped_ptr_unittest.cc
@@ -602,5 +602,3 @@ TEST(ScopedPtrTest, OverloadedNewAndDelete) {
EXPECT_EQ(1, OverloadedNewAndDelete::delete_count());
EXPECT_EQ(1, OverloadedNewAndDelete::new_count());
}
-
-// TODO scoped_ptr_malloc
diff --git a/chromium/base/memory/shared_memory.h b/chromium/base/memory/shared_memory.h
index 9007aede109..d48071e2f73 100644
--- a/chromium/base/memory/shared_memory.h
+++ b/chromium/base/memory/shared_memory.h
@@ -22,6 +22,7 @@
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#endif
namespace base {
@@ -32,38 +33,43 @@ class FilePath;
// the underlying OS handle to a shared memory segment.
#if defined(OS_WIN)
typedef HANDLE SharedMemoryHandle;
-typedef HANDLE SharedMemoryLock;
#elif defined(OS_POSIX)
// A SharedMemoryId is sufficient to identify a given shared memory segment on a
// system, but insufficient to map it.
typedef FileDescriptor SharedMemoryHandle;
typedef ino_t SharedMemoryId;
-// On POSIX, the lock is implemented as a lockf() on the mapped file,
-// so no additional member (or definition of SharedMemoryLock) is
-// needed.
#endif
// Options for creating a shared memory object.
struct SharedMemoryCreateOptions {
- SharedMemoryCreateOptions() : name(NULL), size(0), open_existing(false),
- executable(false) {}
-
+ SharedMemoryCreateOptions()
+ : name_deprecated(NULL),
+ size(0),
+ open_existing_deprecated(false),
+ executable(false),
+ share_read_only(false) {}
+
+ // DEPRECATED (crbug.com/345734):
// If NULL, the object is anonymous. This pointer is owned by the caller
// and must live through the call to Create().
- const std::string* name;
+ const std::string* name_deprecated;
// Size of the shared memory object to be created.
// When opening an existing object, this has no effect.
size_t size;
+ // DEPRECATED (crbug.com/345734):
// If true, and the shared memory already exists, Create() will open the
// existing shared memory and ignore the size parameter. If false,
- // shared memory must not exist. This flag is meaningless unless name is
- // non-NULL.
- bool open_existing;
+ // shared memory must not exist. This flag is meaningless unless
+ // name_deprecated is non-NULL.
+ bool open_existing_deprecated;
// If true, mappings might need to be made executable later.
bool executable;
+
+ // If true, the file can be shared read-only to a process.
+ bool share_read_only;
};
// Platform abstraction for shared memory. Provides a C++ wrapper
@@ -74,8 +80,8 @@ class BASE_EXPORT SharedMemory {
#if defined(OS_WIN)
// Similar to the default constructor, except that this allows for
- // calling Lock() to acquire the named mutex before either Create or Open
- // are called on Windows.
+ // calling LockDeprecated() to acquire the named mutex before either Create or
+ // Open are called on Windows.
explicit SharedMemory(const std::wstring& name);
#endif
@@ -126,16 +132,18 @@ class BASE_EXPORT SharedMemory {
return Create(options);
}
+ // DEPRECATED (crbug.com/345734):
// Creates or opens a shared memory segment based on a name.
// If open_existing is true, and the shared memory already exists,
// opens the existing shared memory and ignores the size parameter.
// If open_existing is false, shared memory must not exist.
// size is the size of the block to be created.
// Returns true on success, false on failure.
- bool CreateNamed(const std::string& name, bool open_existing, size_t size) {
+ bool CreateNamedDeprecated(
+ const std::string& name, bool open_existing, size_t size) {
SharedMemoryCreateOptions options;
- options.name = &name;
- options.open_existing = open_existing;
+ options.name_deprecated = &name;
+ options.open_existing_deprecated = open_existing;
options.size = size;
return Create(options);
}
@@ -152,7 +160,8 @@ class BASE_EXPORT SharedMemory {
// Maps the shared memory into the caller's address space.
// Returns true on success, false otherwise. The memory address
// is accessed via the memory() accessor. The mapped address is guaranteed to
- // have an alignment of at least MAP_MINIMUM_ALIGNMENT.
+ // have an alignment of at least MAP_MINIMUM_ALIGNMENT. This method will fail
+ // if this object is currently mapped.
bool Map(size_t bytes) {
return MapAt(0, bytes);
}
@@ -201,8 +210,8 @@ class BASE_EXPORT SharedMemory {
// handle for use in the remote process.
//
// |*this| must have been initialized using one of the Create*() or Open()
- // methods. If it was constructed from a SharedMemoryHandle, this call will
- // CHECK-fail.
+ // methods with share_read_only=true. If it was constructed from a
+ // SharedMemoryHandle, this call will CHECK-fail.
//
// Returns true on success, false otherwise.
bool ShareReadOnlyToProcess(ProcessHandle process,
@@ -243,31 +252,27 @@ class BASE_EXPORT SharedMemory {
return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE);
}
+ // DEPRECATED (crbug.com/345734):
// Locks the shared memory.
//
// WARNING: on POSIX the memory locking primitive only works across
- // processes, not across threads. The Lock method is not currently
+ // processes, not across threads. The LockDeprecated method is not currently
// used in inner loops, so we protect against multiple threads in a
// critical section using a class global lock.
- void Lock();
-
-#if defined(OS_WIN)
- // A Lock() implementation with a timeout that also allows setting
- // security attributes on the mutex. sec_attr may be NULL.
- // Returns true if the Lock() has been acquired, false if the timeout was
- // reached.
- bool Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr);
-#endif
+ void LockDeprecated();
+ // DEPRECATED (crbug.com/345734):
// Releases the shared memory lock.
- void Unlock();
+ void UnlockDeprecated();
private:
#if defined(OS_POSIX) && !defined(OS_NACL)
- bool PrepareMapFile(file_util::ScopedFILE fp, file_util::ScopedFD readonly);
+#if !defined(OS_ANDROID)
+ bool PrepareMapFile(ScopedFILE fp, ScopedFD readonly);
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
- void LockOrUnlockCommon(int function);
#endif
+ void LockOrUnlockCommon(int function);
+#endif // defined(OS_POSIX) && !defined(OS_NACL)
enum ShareMode {
SHARE_READONLY,
SHARE_CURRENT_MODE,
@@ -290,28 +295,29 @@ class BASE_EXPORT SharedMemory {
bool read_only_;
size_t requested_size_;
#if !defined(OS_POSIX)
- SharedMemoryLock lock_;
+ HANDLE lock_;
#endif
DISALLOW_COPY_AND_ASSIGN(SharedMemory);
};
+// DEPRECATED (crbug.com/345734):
// A helper class that acquires the shared memory lock while
-// the SharedMemoryAutoLock is in scope.
-class SharedMemoryAutoLock {
+// the SharedMemoryAutoLockDeprecated is in scope.
+class SharedMemoryAutoLockDeprecated {
public:
- explicit SharedMemoryAutoLock(SharedMemory* shared_memory)
+ explicit SharedMemoryAutoLockDeprecated(SharedMemory* shared_memory)
: shared_memory_(shared_memory) {
- shared_memory_->Lock();
+ shared_memory_->LockDeprecated();
}
- ~SharedMemoryAutoLock() {
- shared_memory_->Unlock();
+ ~SharedMemoryAutoLockDeprecated() {
+ shared_memory_->UnlockDeprecated();
}
private:
SharedMemory* shared_memory_;
- DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLock);
+ DISALLOW_COPY_AND_ASSIGN(SharedMemoryAutoLockDeprecated);
};
} // namespace base
diff --git a/chromium/base/memory/shared_memory_android.cc b/chromium/base/memory/shared_memory_android.cc
index 25a65730cb7..5ba1bd6a101 100644
--- a/chromium/base/memory/shared_memory_android.cc
+++ b/chromium/base/memory/shared_memory_android.cc
@@ -24,7 +24,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
// "name" is just a label in ashmem. It is visible in /proc/pid/maps.
mapped_file_ = ashmem_create_region(
- options.name == NULL ? "" : options.name->c_str(),
+ options.name_deprecated == NULL ? "" : options.name_deprecated->c_str(),
options.size);
if (-1 == mapped_file_) {
DLOG(ERROR) << "Shared memory creation failed";
diff --git a/chromium/base/memory/shared_memory_nacl.cc b/chromium/base/memory/shared_memory_nacl.cc
index 93c10026191..39625ee65b5 100644
--- a/chromium/base/memory/shared_memory_nacl.cc
+++ b/chromium/base/memory/shared_memory_nacl.cc
@@ -91,6 +91,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
return false;
+ if (memory_)
+ return false;
+
memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
MAP_SHARED, mapped_file_, offset);
@@ -130,11 +133,11 @@ void SharedMemory::Close() {
}
}
-void SharedMemory::Lock() {
+void SharedMemory::LockDeprecated() {
NOTIMPLEMENTED();
}
-void SharedMemory::Unlock() {
+void SharedMemory::UnlockDeprecated() {
NOTIMPLEMENTED();
}
diff --git a/chromium/base/memory/shared_memory_posix.cc b/chromium/base/memory/shared_memory_posix.cc
index 35d8c7d35df..f0a28ba0625 100644
--- a/chromium/base/memory/shared_memory_posix.cc
+++ b/chromium/base/memory/shared_memory_posix.cc
@@ -12,6 +12,7 @@
#include <unistd.h>
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/process/process_metrics.h"
@@ -30,9 +31,6 @@
#include "third_party/ashmem/ashmem.h"
#endif
-using file_util::ScopedFD;
-using file_util::ScopedFILE;
-
namespace base {
namespace {
@@ -132,23 +130,27 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
ScopedFILE fp;
bool fix_size = true;
- int readonly_fd_storage = -1;
- ScopedFD readonly_fd(&readonly_fd_storage);
+ ScopedFD readonly_fd;
FilePath path;
- if (options.name == NULL || options.name->empty()) {
+ if (options.name_deprecated == NULL || options.name_deprecated->empty()) {
// It doesn't make sense to have a open-existing private piece of shmem
- DCHECK(!options.open_existing);
+ DCHECK(!options.open_existing_deprecated);
// Q: Why not use the shm_open() etc. APIs?
// A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
- fp.reset(base::CreateAndOpenTemporaryShmemFile(&path, options.executable));
+ FilePath directory;
+ if (GetShmemTempDir(options.executable, &directory))
+ fp.reset(CreateAndOpenTemporaryFileInDir(directory, &path));
if (fp) {
- // Also open as readonly so that we can ShareReadOnlyToProcess.
- *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
- if (*readonly_fd < 0) {
- DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
- fp.reset();
+ if (options.share_read_only) {
+ // Also open as readonly so that we can ShareReadOnlyToProcess.
+ readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
+ if (!readonly_fd.is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ fp.reset();
+ return false;
+ }
}
// Deleting the file prevents anyone else from mapping it in (making it
// private), and prevents the need for cleanup (once the last fd is
@@ -157,7 +159,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
PLOG(WARNING) << "unlink";
}
} else {
- if (!FilePathForMemoryName(*options.name, &path))
+ if (!FilePathForMemoryName(*options.name_deprecated, &path))
return false;
// Make sure that the file is opened without any permission
@@ -167,7 +169,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
// First, try to create the file.
int fd = HANDLE_EINTR(
open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly));
- if (fd == -1 && options.open_existing) {
+ if (fd == -1 && options.open_existing_deprecated) {
// If this doesn't work, try and open an existing file in append mode.
// Opening an existing file in a world writable directory has two main
// security implications:
@@ -197,12 +199,15 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
fix_size = false;
}
- // Also open as readonly so that we can ShareReadOnlyToProcess.
- *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
- if (*readonly_fd < 0) {
- DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
- close(fd);
- fd = -1;
+ if (options.share_read_only) {
+ // Also open as readonly so that we can ShareReadOnlyToProcess.
+ readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
+ if (!readonly_fd.is_valid()) {
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ close(fd);
+ fd = -1;
+ return false;
+ }
}
if (fd >= 0) {
// "a+" is always appropriate: if it's a new file, a+ is similar to w+.
@@ -265,15 +270,13 @@ bool SharedMemory::Open(const std::string& name, bool read_only) {
const char *mode = read_only ? "r" : "r+";
ScopedFILE fp(base::OpenFile(path, mode));
- int readonly_fd_storage = -1;
- ScopedFD readonly_fd(&readonly_fd_storage);
- *readonly_fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY));
- if (*readonly_fd < 0) {
+ ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
+ if (!readonly_fd.is_valid()) {
DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ return false;
}
return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
}
-
#endif // !defined(OS_ANDROID)
bool SharedMemory::MapAt(off_t offset, size_t bytes) {
@@ -283,6 +286,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
return false;
+ if (memory_)
+ return false;
+
#if defined(OS_ANDROID)
// On Android, Map can be called with a size and offset of zero to use the
// ashmem-determined size.
@@ -339,12 +345,12 @@ void SharedMemory::Close() {
}
}
-void SharedMemory::Lock() {
+void SharedMemory::LockDeprecated() {
g_thread_lock_.Get().Acquire();
LockOrUnlockCommon(F_LOCK);
}
-void SharedMemory::Unlock() {
+void SharedMemory::UnlockDeprecated() {
LockOrUnlockCommon(F_ULOCK);
g_thread_lock_.Get().Release();
}
@@ -353,7 +359,8 @@ void SharedMemory::Unlock() {
bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
DCHECK_EQ(-1, mapped_file_);
DCHECK_EQ(-1, readonly_mapped_file_);
- if (fp == NULL || *readonly_fd < 0) return false;
+ if (fp == NULL)
+ return false;
// This function theoretically can block on the disk, but realistically
// the temporary files we create will just go into the buffer cache
@@ -361,14 +368,16 @@ bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
base::ThreadRestrictions::ScopedAllowIO allow_io;
struct stat st = {};
- struct stat readonly_st = {};
if (fstat(fileno(fp.get()), &st))
NOTREACHED();
- if (fstat(*readonly_fd, &readonly_st))
- NOTREACHED();
- if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
- LOG(ERROR) << "writable and read-only inodes don't match; bailing";
- return false;
+ if (readonly_fd.is_valid()) {
+ struct stat readonly_st = {};
+ if (fstat(readonly_fd.get(), &readonly_st))
+ NOTREACHED();
+ if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
+ LOG(ERROR) << "writable and read-only inodes don't match; bailing";
+ return false;
+ }
}
mapped_file_ = dup(fileno(fp.get()));
@@ -381,11 +390,10 @@ bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
}
}
inode_ = st.st_ino;
- readonly_mapped_file_ = *readonly_fd.release();
+ readonly_mapped_file_ = readonly_fd.release();
return true;
}
-#endif
// For the given shmem named |mem_name|, return a filename to mmap()
// (and possibly create). Modifies |filename|. Return false on
@@ -413,6 +421,7 @@ bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
*path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
return true;
}
+#endif // !defined(OS_ANDROID)
void SharedMemory::LockOrUnlockCommon(int function) {
DCHECK_GE(mapped_file_, 0);
diff --git a/chromium/base/memory/shared_memory_unittest.cc b/chromium/base/memory/shared_memory_unittest.cc
index f3c612f803d..4d49c36f136 100644
--- a/chromium/base/memory/shared_memory_unittest.cc
+++ b/chromium/base/memory/shared_memory_unittest.cc
@@ -61,7 +61,7 @@ class MultipleThreadMain : public PlatformThread::Delegate {
#endif
const uint32 kDataSize = 1024;
SharedMemory memory;
- bool rv = memory.CreateNamed(s_test_name_, true, kDataSize);
+ bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize);
EXPECT_TRUE(rv);
rv = memory.Map(kDataSize);
EXPECT_TRUE(rv);
@@ -109,8 +109,8 @@ class MultipleLockThread : public PlatformThread::Delegate {
SharedMemoryHandle handle = NULL;
{
SharedMemory memory1;
- EXPECT_TRUE(memory1.CreateNamed("SharedMemoryMultipleLockThreadTest",
- true, kDataSize));
+ EXPECT_TRUE(memory1.CreateNamedDeprecated(
+ "SharedMemoryMultipleLockThreadTest", true, kDataSize));
EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
// TODO(paulg): Implement this once we have a posix version of
// SharedMemory::ShareToProcess.
@@ -122,12 +122,12 @@ class MultipleLockThread : public PlatformThread::Delegate {
volatile int* const ptr = static_cast<int*>(memory2.memory());
for (int idx = 0; idx < 20; idx++) {
- memory2.Lock();
+ memory2.LockDeprecated();
int i = (id_ << 16) + idx;
*ptr = i;
PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
EXPECT_EQ(*ptr, i);
- memory2.Unlock();
+ memory2.UnlockDeprecated();
}
memory2.Close();
@@ -143,7 +143,7 @@ class MultipleLockThread : public PlatformThread::Delegate {
} // namespace
// Android doesn't support SharedMemory::Open/Delete/
-// CreateNamed(openExisting=true)
+// CreateNamedDeprecated(openExisting=true)
#if !defined(OS_ANDROID)
TEST(SharedMemoryTest, OpenClose) {
const uint32 kDataSize = 1024;
@@ -158,7 +158,7 @@ TEST(SharedMemoryTest, OpenClose) {
EXPECT_TRUE(rv);
rv = memory1.Open(test_name, false);
EXPECT_FALSE(rv);
- rv = memory1.CreateNamed(test_name, false, kDataSize);
+ rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize);
EXPECT_TRUE(rv);
rv = memory1.Map(kDataSize);
EXPECT_TRUE(rv);
@@ -201,10 +201,10 @@ TEST(SharedMemoryTest, OpenExclusive) {
<< Time::Now().ToDoubleT();
std::string test_name = test_name_stream.str();
- // Open two handles to a memory segment and check that open_existing works
- // as expected.
+ // Open two handles to a memory segment and check that
+ // open_existing_deprecated works as expected.
SharedMemory memory1;
- bool rv = memory1.CreateNamed(test_name, false, kDataSize);
+ bool rv = memory1.CreateNamedDeprecated(test_name, false, kDataSize);
EXPECT_TRUE(rv);
// Memory1 knows it's size because it created it.
@@ -224,11 +224,11 @@ TEST(SharedMemoryTest, OpenExclusive) {
SharedMemory memory2;
// Should not be able to create if openExisting is false.
- rv = memory2.CreateNamed(test_name, false, kDataSize2);
+ rv = memory2.CreateNamedDeprecated(test_name, false, kDataSize2);
EXPECT_FALSE(rv);
// Should be able to create with openExisting true.
- rv = memory2.CreateNamed(test_name, true, kDataSize2);
+ rv = memory2.CreateNamedDeprecated(test_name, true, kDataSize2);
EXPECT_TRUE(rv);
// Memory2 shouldn't know the size because we didn't create it.
@@ -373,7 +373,11 @@ TEST(SharedMemoryTest, ShareReadOnly) {
StringPiece contents = "Hello World";
SharedMemory writable_shmem;
- ASSERT_TRUE(writable_shmem.CreateAndMapAnonymous(contents.size()));
+ SharedMemoryCreateOptions options;
+ options.size = contents.size();
+ options.share_read_only = true;
+ ASSERT_TRUE(writable_shmem.Create(options));
+ ASSERT_TRUE(writable_shmem.Map(options.size));
memcpy(writable_shmem.memory(), contents.data(), contents.size());
EXPECT_TRUE(writable_shmem.Unmap());
@@ -477,7 +481,6 @@ TEST(SharedMemoryTest, MapAt) {
SharedMemory memory;
ASSERT_TRUE(memory.CreateAndMapAnonymous(kDataSize));
- ASSERT_TRUE(memory.Map(kDataSize));
uint32* ptr = static_cast<uint32*>(memory.memory());
ASSERT_NE(ptr, static_cast<void*>(NULL));
@@ -497,6 +500,19 @@ TEST(SharedMemoryTest, MapAt) {
}
}
+TEST(SharedMemoryTest, MapTwice) {
+ const uint32 kDataSize = 1024;
+ SharedMemory memory;
+ bool rv = memory.CreateAndMapAnonymous(kDataSize);
+ EXPECT_TRUE(rv);
+
+ void* old_address = memory.memory();
+
+ rv = memory.Map(kDataSize);
+ EXPECT_FALSE(rv);
+ EXPECT_EQ(old_address, memory.memory());
+}
+
#if defined(OS_POSIX)
// Create a shared memory object, mmap it, and mprotect it to PROT_EXEC.
TEST(SharedMemoryTest, AnonymousExecutable) {
@@ -561,7 +577,7 @@ TEST(SharedMemoryTest, FilePermissionsNamed) {
options.size = kTestSize;
std::string shared_mem_name = "shared_perm_test-" + IntToString(getpid()) +
"-" + Uint64ToString(RandUint64());
- options.name = &shared_mem_name;
+ options.name_deprecated = &shared_mem_name;
// Set a file mode creation mask that gives all permissions.
ScopedUmaskSetter permissive_mask(S_IWGRP | S_IWOTH);
@@ -613,7 +629,7 @@ class SharedMemoryProcessTest : public MultiProcessTest {
#endif
const uint32 kDataSize = 1024;
SharedMemory memory;
- bool rv = memory.CreateNamed(s_test_name_, true, kDataSize);
+ bool rv = memory.CreateNamedDeprecated(s_test_name_, true, kDataSize);
EXPECT_TRUE(rv);
if (rv != true)
errors++;
@@ -624,13 +640,13 @@ class SharedMemoryProcessTest : public MultiProcessTest {
int *ptr = static_cast<int*>(memory.memory());
for (int idx = 0; idx < 20; idx++) {
- memory.Lock();
+ memory.LockDeprecated();
int i = (1 << 16) + idx;
*ptr = i;
PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
if (*ptr != i)
errors++;
- memory.Unlock();
+ memory.UnlockDeprecated();
}
memory.Close();
@@ -648,7 +664,7 @@ TEST_F(SharedMemoryProcessTest, Tasks) {
ProcessHandle handles[kNumTasks];
for (int index = 0; index < kNumTasks; ++index) {
- handles[index] = SpawnChild("SharedMemoryTestMain", false);
+ handles[index] = SpawnChild("SharedMemoryTestMain");
ASSERT_TRUE(handles[index]);
}
diff --git a/chromium/base/memory/shared_memory_win.cc b/chromium/base/memory/shared_memory_win.cc
index 77741bc0d9d..cc177ab3f21 100644
--- a/chromium/base/memory/shared_memory_win.cc
+++ b/chromium/base/memory/shared_memory_win.cc
@@ -113,7 +113,8 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
return false;
size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
- name_ = ASCIIToWide(options.name == NULL ? "" : *options.name);
+ name_ = ASCIIToWide(options.name_deprecated == NULL ? "" :
+ *options.name_deprecated);
mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size),
name_.empty() ? NULL : name_.c_str());
@@ -127,7 +128,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
// If the file already existed, set requested_size_ to 0 to show that
// we don't know the size.
requested_size_ = 0;
- if (!options.open_existing) {
+ if (!options.open_existing_deprecated) {
Close();
return false;
}
@@ -163,6 +164,9 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
return false;
+ if (memory_)
+ return false;
+
memory_ = MapViewOfFile(mapped_file_,
read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
FILE_MAP_WRITE,
@@ -230,27 +234,22 @@ void SharedMemory::Close() {
}
}
-void SharedMemory::Lock() {
- Lock(INFINITE, NULL);
-}
-
-bool SharedMemory::Lock(uint32 timeout_ms, SECURITY_ATTRIBUTES* sec_attr) {
+void SharedMemory::LockDeprecated() {
if (lock_ == NULL) {
std::wstring name = name_;
name.append(L"lock");
- lock_ = CreateMutex(sec_attr, FALSE, name.c_str());
+ lock_ = CreateMutex(NULL, FALSE, name.c_str());
if (lock_ == NULL) {
DPLOG(ERROR) << "Could not create mutex.";
- return false; // there is nothing good we can do here.
+ NOTREACHED();
+ return; // There is nothing good we can do here.
}
}
- DWORD result = WaitForSingleObject(lock_, timeout_ms);
-
- // Return false for WAIT_ABANDONED, WAIT_TIMEOUT or WAIT_FAILED.
- return (result == WAIT_OBJECT_0);
+ DWORD result = WaitForSingleObject(lock_, INFINITE);
+ DCHECK_EQ(result, WAIT_OBJECT_0);
}
-void SharedMemory::Unlock() {
+void SharedMemory::UnlockDeprecated() {
DCHECK(lock_ != NULL);
ReleaseMutex(lock_);
}
diff --git a/chromium/base/memory/singleton.cc b/chromium/base/memory/singleton.cc
index ee5e58d0057..f68ecaa8da9 100644
--- a/chromium/base/memory/singleton.cc
+++ b/chromium/base/memory/singleton.cc
@@ -18,7 +18,10 @@ subtle::AtomicWord WaitForInstance(subtle::AtomicWord* instance) {
// the object has been created.
subtle::AtomicWord value;
while (true) {
- value = subtle::NoBarrier_Load(instance);
+ // The load has acquire memory ordering as the thread which reads the
+ // instance pointer must acquire visibility over the associated data.
+ // The pairing Release_Store operation is in Singleton::get().
+ value = subtle::Acquire_Load(instance);
if (value != kBeingCreatedMarker)
break;
PlatformThread::YieldCurrentThread();
diff --git a/chromium/base/memory/singleton.h b/chromium/base/memory/singleton.h
index 0d4fc8990c4..e5e2e3efed0 100644
--- a/chromium/base/memory/singleton.h
+++ b/chromium/base/memory/singleton.h
@@ -63,10 +63,12 @@ struct DefaultSingletonTraits {
// exit. See below for the required call that makes this happen.
static const bool kRegisterAtExit = true;
+#ifndef NDEBUG
// Set to false to disallow access on a non-joinable thread. This is
// different from kRegisterAtExit because StaticMemorySingletonTraits allows
// access on non-joinable threads, and gracefully handles this.
static const bool kAllowedToAccessOnNonjoinableThread = false;
+#endif
};
@@ -76,7 +78,9 @@ struct DefaultSingletonTraits {
template<typename Type>
struct LeakySingletonTraits : public DefaultSingletonTraits<Type> {
static const bool kRegisterAtExit = false;
+#ifndef NDEBUG
static const bool kAllowedToAccessOnNonjoinableThread = true;
+#endif
};
@@ -229,7 +233,9 @@ class Singleton {
base::ThreadRestrictions::AssertSingletonAllowed();
#endif
- base::subtle::AtomicWord value = base::subtle::NoBarrier_Load(&instance_);
+ // The load has acquire memory ordering as the thread which reads the
+ // instance_ pointer must acquire visibility over the singleton data.
+ base::subtle::AtomicWord value = base::subtle::Acquire_Load(&instance_);
if (value != 0 && value != base::internal::kBeingCreatedMarker) {
// See the corresponding HAPPENS_BEFORE below.
ANNOTATE_HAPPENS_AFTER(&instance_);
@@ -248,6 +254,7 @@ class Singleton {
// synchronization between different threads calling get().
// See the corresponding HAPPENS_AFTER below and above.
ANNOTATE_HAPPENS_BEFORE(&instance_);
+ // Releases the visibility over instance_ to the readers.
base::subtle::Release_Store(
&instance_, reinterpret_cast<base::subtle::AtomicWord>(newval));
diff --git a/chromium/base/memory/weak_ptr.h b/chromium/base/memory/weak_ptr.h
index 1675889e229..f6001e2a169 100644
--- a/chromium/base/memory/weak_ptr.h
+++ b/chromium/base/memory/weak_ptr.h
@@ -83,7 +83,7 @@ class BASE_EXPORT WeakReference {
public:
// Although Flag is bound to a specific thread, it may be deleted from another
// via base::WeakPtr::~WeakPtr().
- class Flag : public RefCountedThreadSafe<Flag> {
+ class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> {
public:
Flag();
diff --git a/chromium/base/memory/weak_ptr_unittest.cc b/chromium/base/memory/weak_ptr_unittest.cc
index e7e12a272d3..dde408b756a 100644
--- a/chromium/base/memory/weak_ptr_unittest.cc
+++ b/chromium/base/memory/weak_ptr_unittest.cc
@@ -44,7 +44,9 @@ struct Base {
struct Derived : public Base {};
struct TargetBase {};
-struct Target : public TargetBase, public SupportsWeakPtr<Target> {};
+struct Target : public TargetBase, public SupportsWeakPtr<Target> {
+ virtual ~Target() {}
+};
struct DerivedTarget : public Target {};
struct Arrow {
WeakPtr<Target> target;
diff --git a/chromium/base/message_loop/incoming_task_queue.cc b/chromium/base/message_loop/incoming_task_queue.cc
index db99d8750c6..bcc712b4a5d 100644
--- a/chromium/base/message_loop/incoming_task_queue.cc
+++ b/chromium/base/message_loop/incoming_task_queue.cc
@@ -28,21 +28,6 @@ bool IncomingTaskQueue::AddToIncomingQueue(
return PostPendingTask(&pending_task);
}
-bool IncomingTaskQueue::TryAddToIncomingQueue(
- const tracked_objects::Location& from_here,
- const Closure& task) {
- if (!incoming_queue_lock_.Try()) {
- // Reset |task|.
- Closure local_task = task;
- return false;
- }
-
- AutoLock locked(incoming_queue_lock_, AutoLock::AlreadyAcquired());
- PendingTask pending_task(
- from_here, task, CalculateDelayedRuntime(TimeDelta()), true);
- return PostPendingTask(&pending_task);
-}
-
bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() {
#if defined(OS_WIN)
return !high_resolution_timer_expiration_.is_null();
@@ -56,13 +41,6 @@ bool IncomingTaskQueue::IsIdleForTesting() {
return incoming_queue_.empty();
}
-void IncomingTaskQueue::LockWaitUnLockForTesting(WaitableEvent* caller_wait,
- WaitableEvent* caller_signal) {
- AutoLock lock(incoming_queue_lock_);
- caller_wait->Signal();
- caller_signal->Wait();
-}
-
void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) {
// Make sure no tasks are lost.
DCHECK(work_queue->empty());
@@ -152,7 +130,8 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) {
// delayed_run_time value) and for identifying the task in about:tracing.
pending_task->sequence_num = next_sequence_num_++;
- TRACE_EVENT_FLOW_BEGIN0("task", "MessageLoop::PostTask",
+ TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ "MessageLoop::PostTask",
TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task)));
bool was_empty = incoming_queue_.empty();
diff --git a/chromium/base/message_loop/incoming_task_queue.h b/chromium/base/message_loop/incoming_task_queue.h
index d831a71a3f7..56c5638295b 100644
--- a/chromium/base/message_loop/incoming_task_queue.h
+++ b/chromium/base/message_loop/incoming_task_queue.h
@@ -38,12 +38,6 @@ class BASE_EXPORT IncomingTaskQueue
TimeDelta delay,
bool nestable);
- // Same as AddToIncomingQueue() except that it will avoid blocking if the lock
- // is already held, and will in that case (when the lock is contended) fail to
- // add the task, and will return false.
- bool TryAddToIncomingQueue(const tracked_objects::Location& from_here,
- const Closure& task);
-
// Returns true if the message loop has high resolution timers enabled.
// Provided for testing.
bool IsHighResolutionTimerEnabledForTesting();
@@ -51,11 +45,6 @@ class BASE_EXPORT IncomingTaskQueue
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
- // Takes the incoming queue lock, signals |caller_wait| and waits until
- // |caller_signal| is signalled.
- void LockWaitUnLockForTesting(WaitableEvent* caller_wait,
- WaitableEvent* caller_signal);
-
// Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called
// from the thread that is running the loop.
void ReloadWorkQueue(TaskQueue* work_queue);
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
index 3f9d01c8620..dd1a393ab08 100644
--- a/chromium/base/message_loop/message_loop.cc
+++ b/chromium/base/message_loop/message_loop.cc
@@ -32,10 +32,8 @@
#if defined(OS_ANDROID)
#include "base/message_loop/message_pump_android.h"
#endif
-
-#if defined(TOOLKIT_GTK)
-#include <gdk/gdk.h>
-#include <gdk/gdkx.h>
+#if defined(USE_GLIB)
+#include "base/message_loop/message_pump_glib.h"
#endif
namespace base {
@@ -50,6 +48,7 @@ LazyInstance<base::ThreadLocalPointer<MessageLoop> >::Leaky lazy_tls_ptr =
// Logical events for Histogram profiling. Run with -message-loop-histogrammer
// to get an accounting of messages and actions taken on each thread.
const int kTaskRunEvent = 0x1;
+#if !defined(OS_NACL)
const int kTimerEvent = 0x2;
// Provide range of message IDs for use in histogramming and debug display.
@@ -83,6 +82,7 @@ const LinearHistogram::DescriptionPair event_descriptions_[] = {
{-1, NULL} // The list must be null terminated, per API to histogram.
};
+#endif // !defined(OS_NACL)
bool enable_histogrammer_ = false;
@@ -98,29 +98,19 @@ bool AlwaysNotifyPump(MessageLoop::Type type) {
#endif
}
-} // namespace
-
-//------------------------------------------------------------------------------
-
-#if defined(OS_WIN)
-
-// Upon a SEH exception in this thread, it restores the original unhandled
-// exception filter.
-static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
- ::SetUnhandledExceptionFilter(old_filter);
- return EXCEPTION_CONTINUE_SEARCH;
-}
+#if defined(OS_IOS)
+typedef MessagePumpIOSForIO MessagePumpForIO;
+#elif defined(OS_NACL)
+typedef MessagePumpDefault MessagePumpForIO;
+#elif defined(OS_POSIX)
+typedef MessagePumpLibevent MessagePumpForIO;
+#endif
-// Retrieves a pointer to the current unhandled exception filter. There
-// is no standalone getter method.
-static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
- LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
- top_filter = ::SetUnhandledExceptionFilter(0);
- ::SetUnhandledExceptionFilter(top_filter);
- return top_filter;
+MessagePumpForIO* ToPumpIO(MessagePump* pump) {
+ return static_cast<MessagePumpForIO*>(pump);
}
-#endif // defined(OS_WIN)
+} // namespace
//------------------------------------------------------------------------------
@@ -137,7 +127,6 @@ MessageLoop::DestructionObserver::~DestructionObserver() {
MessageLoop::MessageLoop(Type type)
: type_(type),
- exception_restoration_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
@@ -146,13 +135,12 @@ MessageLoop::MessageLoop(Type type)
run_loop_(NULL) {
Init();
- pump_.reset(CreateMessagePumpForType(type));
+ pump_ = CreateMessagePumpForType(type).Pass();
}
MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
: pump_(pump.Pass()),
type_(TYPE_CUSTOM),
- exception_restoration_(false),
nestable_tasks_allowed_(true),
#if defined(OS_WIN)
os_modal_loop_(false),
@@ -223,29 +211,22 @@ bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) {
}
// static
-MessagePump* MessageLoop::CreateMessagePumpForType(Type type) {
+scoped_ptr<MessagePump> MessageLoop::CreateMessagePumpForType(Type type) {
// TODO(rvargas): Get rid of the OS guards.
-#if defined(OS_WIN)
-#define MESSAGE_PUMP_UI new MessagePumpForUI()
-#define MESSAGE_PUMP_IO new MessagePumpForIO()
-#elif defined(OS_IOS)
-#define MESSAGE_PUMP_UI MessagePumpMac::Create()
-#define MESSAGE_PUMP_IO new MessagePumpIOSForIO()
-#elif defined(OS_MACOSX)
-#define MESSAGE_PUMP_UI MessagePumpMac::Create()
-#define MESSAGE_PUMP_IO new MessagePumpLibevent()
+#if defined(USE_GLIB) && !defined(OS_NACL)
+ typedef MessagePumpGlib MessagePumpForUI;
+#elif defined(OS_LINUX) && !defined(OS_NACL)
+ typedef MessagePumpLibevent MessagePumpForUI;
+#endif
+
+#if defined(OS_IOS) || defined(OS_MACOSX)
+#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(MessagePumpMac::Create())
#elif defined(OS_NACL)
// Currently NaCl doesn't have a UI MessageLoop.
// TODO(abarth): Figure out if we need this.
-#define MESSAGE_PUMP_UI NULL
-// ipc_channel_nacl.cc uses a worker thread to do socket reads currently, and
-// doesn't require extra support for watching file descriptors.
-#define MESSAGE_PUMP_IO new MessagePumpDefault()
-#elif defined(OS_POSIX) // POSIX but not MACOSX.
-#define MESSAGE_PUMP_UI new MessagePumpForUI()
-#define MESSAGE_PUMP_IO new MessagePumpLibevent()
+#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>()
#else
-#error Not implemented
+#define MESSAGE_PUMP_UI scoped_ptr<MessagePump>(new MessagePumpForUI())
#endif
if (type == MessageLoop::TYPE_UI) {
@@ -254,17 +235,15 @@ MessagePump* MessageLoop::CreateMessagePumpForType(Type type) {
return MESSAGE_PUMP_UI;
}
if (type == MessageLoop::TYPE_IO)
- return MESSAGE_PUMP_IO;
-#if defined(TOOLKIT_GTK)
- if (type == MessageLoop::TYPE_GPU)
- return new MessagePumpX11();
-#endif
+ return scoped_ptr<MessagePump>(new MessagePumpForIO());
+
#if defined(OS_ANDROID)
if (type == MessageLoop::TYPE_JAVA)
- return MESSAGE_PUMP_UI;
+ return scoped_ptr<MessagePump>(new MessagePumpForUI());
#endif
+
DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type);
- return new MessagePumpDefault();
+ return scoped_ptr<MessagePump>(new MessagePumpDefault());
}
void MessageLoop::AddDestructionObserver(
@@ -286,13 +265,6 @@ void MessageLoop::PostTask(
incoming_task_queue_->AddToIncomingQueue(from_here, task, TimeDelta(), true);
}
-bool MessageLoop::TryPostTask(
- const tracked_objects::Location& from_here,
- const Closure& task) {
- DCHECK(!task.is_null()) << from_here.ToString();
- return incoming_task_queue_->TryAddToIncomingQueue(from_here, task);
-}
-
void MessageLoop::PostDelayedTask(
const tracked_objects::Location& from_here,
const Closure& task,
@@ -399,11 +371,6 @@ bool MessageLoop::IsIdleForTesting() {
return incoming_task_queue_->IsIdleForTesting();
}
-void MessageLoop::LockWaitUnLockForTesting(WaitableEvent* caller_wait,
- WaitableEvent* caller_signal) {
- incoming_task_queue_->LockWaitUnLockForTesting(caller_wait, caller_signal);
-}
-
//------------------------------------------------------------------------------
void MessageLoop::Init() {
@@ -417,40 +384,12 @@ void MessageLoop::Init() {
new ThreadTaskRunnerHandle(message_loop_proxy_));
}
-// Runs the loop in two different SEH modes:
-// enable_SEH_restoration_ = false : any unhandled exception goes to the last
-// one that calls SetUnhandledExceptionFilter().
-// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
-// that was existed before the loop was run.
void MessageLoop::RunHandler() {
-#if defined(OS_WIN)
- if (exception_restoration_) {
- RunInternalInSEHFrame();
- return;
- }
-#endif
-
- RunInternal();
-}
-
-#if defined(OS_WIN)
-__declspec(noinline) void MessageLoop::RunInternalInSEHFrame() {
- LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
- __try {
- RunInternal();
- } __except(SEHFilter(current_filter)) {
- }
- return;
-}
-#endif
-
-void MessageLoop::RunInternal() {
DCHECK_EQ(this, current());
StartHistogrammer();
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
+#if defined(OS_WIN)
if (run_loop_->dispatcher_ && type() == TYPE_UI) {
static_cast<MessagePumpForUI*>(pump_.get())->
RunWithDispatcher(this, run_loop_->dispatcher_);
@@ -479,14 +418,14 @@ void MessageLoop::RunTask(const PendingTask& pending_task) {
tracked_objects::TrackedTime start_time =
tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally);
- TRACE_EVENT_FLOW_END1("task", "MessageLoop::PostTask",
- TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
+ TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ "MessageLoop::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(pending_task)),
"queue_duration",
(start_time - pending_task.EffectiveTimePosted()).InMilliseconds());
// When tracing memory for posted tasks it's more valuable to attribute the
// memory allocations to the source function than generically to "RunTask".
TRACE_EVENT_WITH_MEMORY_TAG2(
- "task", "MessageLoop::RunTask",
+ "toplevel", "MessageLoop::RunTask",
pending_task.posted_from.function_name(), // Name for memory tracking.
"src_file", pending_task.posted_from.file_name(),
"src_func", pending_task.posted_from.function_name());
@@ -712,6 +651,7 @@ void MessageLoop::ReleaseSoonInternal(
PostNonNestableTask(from_here, Bind(releaser, object));
}
+#if !defined(OS_NACL)
//------------------------------------------------------------------------------
// MessageLoopForUI
@@ -728,64 +668,75 @@ void MessageLoopForUI::Attach() {
}
#endif
-#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID)
+#if defined(OS_WIN)
void MessageLoopForUI::AddObserver(Observer* observer) {
- pump_ui()->AddObserver(observer);
+ static_cast<MessagePumpWin*>(pump_.get())->AddObserver(observer);
}
void MessageLoopForUI::RemoveObserver(Observer* observer) {
- pump_ui()->RemoveObserver(observer);
+ static_cast<MessagePumpWin*>(pump_.get())->RemoveObserver(observer);
}
+#endif // defined(OS_WIN)
-#endif // !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID)
+#if defined(USE_OZONE) || (defined(OS_CHROMEOS) && !defined(USE_GLIB))
+bool MessageLoopForUI::WatchFileDescriptor(
+ int fd,
+ bool persistent,
+ MessagePumpLibevent::Mode mode,
+ MessagePumpLibevent::FileDescriptorWatcher *controller,
+ MessagePumpLibevent::Watcher *delegate) {
+ return static_cast<MessagePumpLibevent*>(pump_.get())->WatchFileDescriptor(
+ fd,
+ persistent,
+ mode,
+ controller,
+ delegate);
+}
+#endif
+
+#endif // !defined(OS_NACL)
//------------------------------------------------------------------------------
// MessageLoopForIO
-#if defined(OS_WIN)
+#if !defined(OS_NACL)
+void MessageLoopForIO::AddIOObserver(
+ MessageLoopForIO::IOObserver* io_observer) {
+ ToPumpIO(pump_.get())->AddIOObserver(io_observer);
+}
+
+void MessageLoopForIO::RemoveIOObserver(
+ MessageLoopForIO::IOObserver* io_observer) {
+ ToPumpIO(pump_.get())->RemoveIOObserver(io_observer);
+}
+#if defined(OS_WIN)
void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) {
- pump_io()->RegisterIOHandler(file, handler);
+ ToPumpIO(pump_.get())->RegisterIOHandler(file, handler);
}
bool MessageLoopForIO::RegisterJobObject(HANDLE job, IOHandler* handler) {
- return pump_io()->RegisterJobObject(job, handler);
+ return ToPumpIO(pump_.get())->RegisterJobObject(job, handler);
}
bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
- return pump_io()->WaitForIOCompletion(timeout, filter);
-}
-
-#elif defined(OS_IOS)
-
-bool MessageLoopForIO::WatchFileDescriptor(int fd,
- bool persistent,
- Mode mode,
- FileDescriptorWatcher *controller,
- Watcher *delegate) {
- return pump_io()->WatchFileDescriptor(
- fd,
- persistent,
- mode,
- controller,
- delegate);
+ return ToPumpIO(pump_.get())->WaitForIOCompletion(timeout, filter);
}
-
-#elif defined(OS_POSIX) && !defined(OS_NACL)
-
+#elif defined(OS_POSIX)
bool MessageLoopForIO::WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate) {
- return pump_libevent()->WatchFileDescriptor(
+ return ToPumpIO(pump_.get())->WatchFileDescriptor(
fd,
persistent,
mode,
controller,
delegate);
}
-
#endif
+#endif // !defined(OS_NACL)
+
} // namespace base
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
index e307d365a00..22d4f7395e4 100644
--- a/chromium/base/message_loop/message_loop.h
+++ b/chromium/base/message_loop/message_loop.h
@@ -18,6 +18,7 @@
#include "base/message_loop/message_loop_proxy.h"
#include "base/message_loop/message_loop_proxy_impl.h"
#include "base/message_loop/message_pump.h"
+#include "base/message_loop/timer_slack.h"
#include "base/observer_list.h"
#include "base/pending_task.h"
#include "base/sequenced_task_runner_helpers.h"
@@ -25,41 +26,21 @@
#include "base/time/time.h"
#include "base/tracking_info.h"
+// TODO(sky): these includes should not be necessary. Nuke them.
#if defined(OS_WIN)
-// We need this to declare base::MessagePumpWin::Dispatcher, which we should
-// really just eliminate.
#include "base/message_loop/message_pump_win.h"
#elif defined(OS_IOS)
#include "base/message_loop/message_pump_io_ios.h"
#elif defined(OS_POSIX)
#include "base/message_loop/message_pump_libevent.h"
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID)
-
-#if defined(USE_AURA) && defined(USE_X11) && !defined(OS_NACL)
-#include "base/message_loop/message_pump_x11.h"
-#elif defined(USE_OZONE) && !defined(OS_NACL)
-#include "base/message_loop/message_pump_ozone.h"
-#else
-#define USE_GTK_MESSAGE_PUMP
-#include "base/message_loop/message_pump_gtk.h"
-#if defined(TOOLKIT_GTK)
-#include "base/message_loop/message_pump_x11.h"
-#endif
-#endif
-
-#endif
#endif
namespace base {
class HistogramBase;
-class MessagePumpDispatcher;
class MessagePumpObserver;
class RunLoop;
class ThreadTaskRunnerHandle;
-#if defined(OS_ANDROID)
-class MessagePumpForUI;
-#endif
class WaitableEvent;
// A MessageLoop is used to process events for a particular thread. There is
@@ -95,14 +76,6 @@ class WaitableEvent;
//
class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
public:
-
-#if defined(USE_GTK_MESSAGE_PUMP)
- typedef MessagePumpGdkObserver Observer;
-#elif !defined(OS_MACOSX) && !defined(OS_ANDROID)
- typedef MessagePumpDispatcher Dispatcher;
- typedef MessagePumpObserver Observer;
-#endif
-
// A MessageLoop has a particular type, which indicates the set of
// asynchronous events it may process in addition to tasks and timers.
//
@@ -113,11 +86,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// This type of ML also supports native UI events (e.g., Windows messages).
// See also MessageLoopForUI.
//
- // TYPE_GPU
- // This type of ML also supports native UI events for use in the GPU
- // process. On Linux this will always be an X11 ML (as compared with the
- // sometimes-GTK ML in the browser process).
- //
// TYPE_IO
// This type of ML also supports asynchronous IO. See also
// MessageLoopForIO.
@@ -135,9 +103,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
TYPE_DEFAULT,
TYPE_UI,
TYPE_CUSTOM,
-#if defined(TOOLKIT_GTK)
- TYPE_GPU,
-#endif
TYPE_IO,
#if defined(OS_ANDROID)
TYPE_JAVA,
@@ -157,7 +122,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
static void EnableHistogrammer(bool enable_histogrammer);
- typedef MessagePump* (MessagePumpFactory)();
+ typedef scoped_ptr<MessagePump> (MessagePumpFactory)();
// Uses the given base::MessagePumpForUIFactory to override the default
// MessagePump implementation for 'TYPE_UI'. Returns true if the factory
// was successfully registered.
@@ -165,10 +130,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Creates the default MessagePump based on |type|. Caller owns return
// value.
- // TODO(sky): convert this and InitMessagePumpForUIFactory() to return a
- // scoped_ptr.
- static MessagePump* CreateMessagePumpForType(Type type);
-
+ static scoped_ptr<MessagePump> CreateMessagePumpForType(Type type);
// A DestructionObserver is notified when the current MessageLoop is being
// destroyed. These observers are notified prior to MessageLoop::current()
// being changed to return NULL. This gives interested parties the chance to
@@ -210,18 +172,11 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// PostTask(from_here, task) is equivalent to
// PostDelayedTask(from_here, task, 0).
//
- // The TryPostTask is meant for the cases where the calling thread cannot
- // block. If posting the task will block, the call returns false, the task
- // is not posted but the task is consumed anyways.
- //
// NOTE: These methods may be called on any thread. The Task will be invoked
// on the thread that executes MessageLoop::Run().
void PostTask(const tracked_objects::Location& from_here,
const Closure& task);
- bool TryPostTask(const tracked_objects::Location& from_here,
- const Closure& task);
-
void PostDelayedTask(const tracked_objects::Location& from_here,
const Closure& task,
TimeDelta delay);
@@ -253,6 +208,18 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// live until the next run of the MessageLoop, or if the object needs to be
// released on a particular thread.
//
+ // A common pattern is to manually increment the object's reference count
+ // (AddRef), clear the pointer, then issue a ReleaseSoon. The reference count
+ // is incremented manually to ensure clearing the pointer does not trigger a
+ // delete and to account for the upcoming decrement (ReleaseSoon). For
+ // example:
+ //
+ // scoped_refptr<Foo> foo = ...
+ // foo->AddRef();
+ // Foo* raw_foo = foo.get();
+ // foo = NULL;
+ // message_loop->ReleaseSoon(raw_foo);
+ //
// NOTE: This method may be called on any thread. The object will be
// released (and thus possibly deleted) on the thread that executes
// MessageLoop::Run(). If this is not the same as the thread that calls
@@ -308,6 +275,11 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// arbitrary MessageLoop to QuitWhenIdle.
static Closure QuitWhenIdleClosure();
+ // Set the timer slack for this message loop.
+ void SetTimerSlack(TimerSlack timer_slack) {
+ pump_->SetTimerSlack(timer_slack);
+ }
+
// Returns true if this loop is |type|. This allows subclasses (especially
// those in tests) to specialize how they are identified.
virtual bool IsType(Type type) const;
@@ -366,14 +338,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
bool old_state_;
};
- // Enables or disables the restoration during an exception of the unhandled
- // exception filter that was active when Run() was called. This can happen
- // if some third party code call SetUnhandledExceptionFilter() and never
- // restores the previous filter.
- void set_exception_restoration(bool restore) {
- exception_restoration_ = restore;
- }
-
// Returns true if we are currently running a nested message loop.
bool IsNested();
@@ -424,31 +388,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Returns true if the message loop is "idle". Provided for testing.
bool IsIdleForTesting();
- // Takes the incoming queue lock, signals |caller_wait| and waits until
- // |caller_signal| is signalled.
- void LockWaitUnLockForTesting(WaitableEvent* caller_wait,
- WaitableEvent* caller_signal);
-
//----------------------------------------------------------------------------
protected:
-
-#if defined(OS_WIN)
- MessagePumpWin* pump_win() {
- return static_cast<MessagePumpWin*>(pump_.get());
- }
-#elif defined(OS_POSIX) && !defined(OS_IOS)
- MessagePumpLibevent* pump_libevent() {
- return static_cast<MessagePumpLibevent*>(pump_.get());
- }
-#if defined(TOOLKIT_GTK)
- friend class MessagePumpX11;
- MessagePumpX11* pump_gpu() {
- DCHECK_EQ(TYPE_GPU, type());
- return static_cast<MessagePumpX11*>(pump_.get());
- }
-#endif
-#endif
-
scoped_ptr<MessagePump> pump_;
private:
@@ -458,21 +399,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Configures various members for the two constructors.
void Init();
- // A function to encapsulate all the exception handling capability in the
- // stacks around the running of a main message loop. It will run the message
- // loop in a SEH try block or not depending on the set_SEH_restoration()
- // flag invoking respectively RunInternalInSEHFrame() or RunInternal().
+ // Invokes the actual run loop using the message pump.
void RunHandler();
-#if defined(OS_WIN)
- __declspec(noinline) void RunInternalInSEHFrame();
-#endif
-
- // A surrounding stack frame around the running of the message loop that
- // supports all saving and restoring of state, as is needed for any/all (ugly)
- // recursive calls.
- void RunInternal();
-
// Called to process any delayed non-nestable tasks.
bool ProcessNextDelayedNonNestableTask();
@@ -539,8 +468,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
ObserverList<DestructionObserver> destruction_observers_;
- bool exception_restoration_;
-
// A recursion block that prevents accidentally running additional tasks when
// insider a (accidentally induced?) nested message pump.
bool nestable_tasks_allowed_;
@@ -578,6 +505,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
};
+#if !defined(OS_NACL)
+
//-----------------------------------------------------------------------------
// MessageLoopForUI extends MessageLoop with methods that are particular to a
// MessageLoop instantiated with TYPE_UI.
@@ -587,10 +516,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
//
class BASE_EXPORT MessageLoopForUI : public MessageLoop {
public:
-#if defined(OS_WIN)
- typedef MessagePumpForUI::MessageFilter MessageFilter;
-#endif
-
MessageLoopForUI() : MessageLoop(TYPE_UI) {
}
@@ -602,6 +527,11 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
return static_cast<MessageLoopForUI*>(loop);
}
+ static bool IsCurrent() {
+ MessageLoop* loop = MessageLoop::current();
+ return loop && loop->type() == MessageLoop::TYPE_UI;
+ }
+
#if defined(OS_IOS)
// On iOS, the main message loop cannot be Run(). Instead call Attach(),
// which connects this MessageLoop to the UI thread's CFRunLoop and allows
@@ -614,33 +544,25 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
// never be called. Instead use Start(), which will forward all the native UI
// events to the Java message loop.
void Start();
-#elif !defined(OS_MACOSX)
+#endif
+
+#if defined(OS_WIN)
+ typedef MessagePumpObserver Observer;
- // Please see message_pump_win/message_pump_glib for definitions of these
- // methods.
+ // Please see message_pump_win for definitions of these methods.
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
-
-#if defined(OS_WIN)
- // Plese see MessagePumpForUI for definitions of this method.
- void SetMessageFilter(scoped_ptr<MessageFilter> message_filter) {
- pump_ui()->SetMessageFilter(message_filter.Pass());
- }
#endif
- protected:
-#if defined(USE_X11)
- friend class MessagePumpX11;
-#endif
-#if defined(USE_OZONE) && !defined(OS_NACL)
- friend class MessagePumpOzone;
+#if defined(USE_OZONE) || (defined(OS_CHROMEOS) && !defined(USE_GLIB))
+ // Please see MessagePumpLibevent for definition.
+ bool WatchFileDescriptor(
+ int fd,
+ bool persistent,
+ MessagePumpLibevent::Mode mode,
+ MessagePumpLibevent::FileDescriptorWatcher* controller,
+ MessagePumpLibevent::Watcher* delegate);
#endif
-
- // TODO(rvargas): Make this platform independent.
- MessagePumpForUI* pump_ui() {
- return static_cast<MessagePumpForUI*>(pump_.get());
- }
-#endif // !defined(OS_MACOSX)
};
// Do not add any member variables to MessageLoopForUI! This is important b/c
@@ -649,6 +571,8 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
MessageLoopForUI_should_not_have_extra_member_variables);
+#endif // !defined(OS_NACL)
+
//-----------------------------------------------------------------------------
// MessageLoopForIO extends MessageLoop with methods that are particular to a
// MessageLoop instantiated with TYPE_IO.
@@ -658,6 +582,23 @@ COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI),
//
class BASE_EXPORT MessageLoopForIO : public MessageLoop {
public:
+ MessageLoopForIO() : MessageLoop(TYPE_IO) {
+ }
+
+ // Returns the MessageLoopForIO of the current thread.
+ static MessageLoopForIO* current() {
+ MessageLoop* loop = MessageLoop::current();
+ DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
+ return static_cast<MessageLoopForIO*>(loop);
+ }
+
+ static bool IsCurrent() {
+ MessageLoop* loop = MessageLoop::current();
+ return loop && loop->type() == MessageLoop::TYPE_IO;
+ }
+
+#if !defined(OS_NACL)
+
#if defined(OS_WIN)
typedef MessagePumpForIO::IOHandler IOHandler;
typedef MessagePumpForIO::IOContext IOContext;
@@ -684,65 +625,25 @@ class BASE_EXPORT MessageLoopForIO : public MessageLoop {
WATCH_WRITE = MessagePumpLibevent::WATCH_WRITE,
WATCH_READ_WRITE = MessagePumpLibevent::WATCH_READ_WRITE
};
-
#endif
- MessageLoopForIO() : MessageLoop(TYPE_IO) {
- }
-
- // Returns the MessageLoopForIO of the current thread.
- static MessageLoopForIO* current() {
- MessageLoop* loop = MessageLoop::current();
- DCHECK_EQ(MessageLoop::TYPE_IO, loop->type());
- return static_cast<MessageLoopForIO*>(loop);
- }
-
- void AddIOObserver(IOObserver* io_observer) {
- pump_io()->AddIOObserver(io_observer);
- }
-
- void RemoveIOObserver(IOObserver* io_observer) {
- pump_io()->RemoveIOObserver(io_observer);
- }
+ void AddIOObserver(IOObserver* io_observer);
+ void RemoveIOObserver(IOObserver* io_observer);
#if defined(OS_WIN)
// Please see MessagePumpWin for definitions of these methods.
void RegisterIOHandler(HANDLE file, IOHandler* handler);
bool RegisterJobObject(HANDLE job, IOHandler* handler);
bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
-
- protected:
- // TODO(rvargas): Make this platform independent.
- MessagePumpForIO* pump_io() {
- return static_cast<MessagePumpForIO*>(pump_.get());
- }
-
-#elif defined(OS_IOS)
- // Please see MessagePumpIOSForIO for definition.
+#elif defined(OS_POSIX)
+ // Please see MessagePumpIOSForIO/MessagePumpLibevent for definition.
bool WatchFileDescriptor(int fd,
bool persistent,
Mode mode,
FileDescriptorWatcher *controller,
Watcher *delegate);
-
- private:
- MessagePumpIOSForIO* pump_io() {
- return static_cast<MessagePumpIOSForIO*>(pump_.get());
- }
-
-#elif defined(OS_POSIX)
- // Please see MessagePumpLibevent for definition.
- bool WatchFileDescriptor(int fd,
- bool persistent,
- Mode mode,
- FileDescriptorWatcher* controller,
- Watcher* delegate);
-
- private:
- MessagePumpLibevent* pump_io() {
- return static_cast<MessagePumpLibevent*>(pump_.get());
- }
-#endif // defined(OS_POSIX)
+#endif // defined(OS_IOS) || defined(OS_POSIX)
+#endif // !defined(OS_NACL)
};
// Do not add any member variables to MessageLoopForIO! This is important b/c
diff --git a/chromium/base/message_loop/message_loop_test.cc b/chromium/base/message_loop/message_loop_test.cc
index 9ac5b72d374..eca6c8f2452 100644
--- a/chromium/base/message_loop/message_loop_test.cc
+++ b/chromium/base/message_loop/message_loop_test.cc
@@ -101,69 +101,14 @@ void RunTest_PostTask(MessagePumpFactory factory) {
&Foo::Test1Int, foo.get(), 100));
MessageLoop::current()->PostTask(FROM_HERE, Bind(
&Foo::Test2Ptr, foo.get(), &a, &c));
-
- // TryPost with no contention. It must succeed.
- EXPECT_TRUE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind(
- &Foo::Test2Mixed, foo.get(), a, &d)));
-
- // TryPost with simulated contention. It must fail. We wait for a helper
- // thread to lock the queue, we TryPost on this thread and finally we
- // signal the helper to unlock and exit.
- WaitableEvent wait(true, false);
- WaitableEvent signal(true, false);
- Thread thread("RunTest_PostTask_helper");
- thread.Start();
- thread.message_loop()->PostTask(
- FROM_HERE,
- Bind(&MessageLoop::LockWaitUnLockForTesting,
- base::Unretained(MessageLoop::current()),
- &wait,
- &signal));
-
- wait.Wait();
- EXPECT_FALSE(MessageLoop::current()->TryPostTask(FROM_HERE, Bind(
- &Foo::Test2Mixed, foo.get(), a, &d)));
- signal.Signal();
-
- // After all tests, post a message that will shut down the message loop
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &MessageLoop::Quit, Unretained(MessageLoop::current())));
-
- // Now kick things off
- MessageLoop::current()->Run();
-
- EXPECT_EQ(foo->test_count(), 105);
- EXPECT_EQ(foo->result(), "abacad");
-}
-
-void RunTest_PostTask_SEH(MessagePumpFactory factory) {
- scoped_ptr<MessagePump> pump(factory());
- MessageLoop loop(pump.Pass());
-
- // Add tests to message loop
- scoped_refptr<Foo> foo(new Foo());
- std::string a("a"), b("b"), c("c"), d("d");
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &Foo::Test0, foo.get()));
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &Foo::Test1ConstRef, foo.get(), a));
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &Foo::Test1Ptr, foo.get(), &b));
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &Foo::Test1Int, foo.get(), 100));
- MessageLoop::current()->PostTask(FROM_HERE, Bind(
- &Foo::Test2Ptr, foo.get(), &a, &c));
MessageLoop::current()->PostTask(FROM_HERE, Bind(
&Foo::Test2Mixed, foo.get(), a, &d));
-
// After all tests, post a message that will shut down the message loop
MessageLoop::current()->PostTask(FROM_HERE, Bind(
&MessageLoop::Quit, Unretained(MessageLoop::current())));
- // Now kick things off with the SEH block active.
- MessageLoop::current()->set_exception_restoration(true);
+ // Now kick things off
MessageLoop::current()->Run();
- MessageLoop::current()->set_exception_restoration(false);
EXPECT_EQ(foo->test_count(), 105);
EXPECT_EQ(foo->result(), "abacad");
diff --git a/chromium/base/message_loop/message_loop_test.h b/chromium/base/message_loop/message_loop_test.h
index 5d1a4f5ac27..3d9889cd58d 100644
--- a/chromium/base/message_loop/message_loop_test.h
+++ b/chromium/base/message_loop/message_loop_test.h
@@ -19,7 +19,6 @@ namespace test {
typedef MessageLoop::MessagePumpFactory MessagePumpFactory;
void RunTest_PostTask(MessagePumpFactory factory);
-void RunTest_PostTask_SEH(MessagePumpFactory factory);
void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory);
void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory);
void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory);
@@ -52,9 +51,6 @@ void RunTest_RecursivePosts(MessagePumpFactory factory);
TEST(MessageLoopTestType##id, PostTask) { \
base::test::RunTest_PostTask(factory); \
} \
- TEST(MessageLoopTestType##id, PostTask_SEH) { \
- base::test::RunTest_PostTask_SEH(factory); \
- } \
TEST(MessageLoopTestType##id, PostDelayedTask_Basic) { \
base::test::RunTest_PostDelayedTask_Basic(factory); \
} \
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
index e6d25ecef82..1b09eb070fa 100644
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ b/chromium/base/message_loop/message_loop_unittest.cc
@@ -22,6 +22,7 @@
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
+#include "base/message_loop/message_pump_dispatcher.h"
#include "base/message_loop/message_pump_win.h"
#include "base/process/memory.h"
#include "base/strings/string16.h"
@@ -35,15 +36,15 @@ namespace base {
namespace {
-MessagePump* TypeDefaultMessagePumpFactory() {
+scoped_ptr<MessagePump> TypeDefaultMessagePumpFactory() {
return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT);
}
-MessagePump* TypeIOMessagePumpFactory() {
+scoped_ptr<MessagePump> TypeIOMessagePumpFactory() {
return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO);
}
-MessagePump* TypeUIMessagePumpFactory() {
+scoped_ptr<MessagePump> TypeUIMessagePumpFactory() {
return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI);
}
@@ -52,36 +53,11 @@ class Foo : public RefCounted<Foo> {
Foo() : test_count_(0) {
}
- void Test0() {
- ++test_count_;
- }
-
void Test1ConstRef(const std::string& a) {
++test_count_;
result_.append(a);
}
- void Test1Ptr(std::string* a) {
- ++test_count_;
- result_.append(*a);
- }
-
- void Test1Int(int a) {
- test_count_ += a;
- }
-
- void Test2Ptr(std::string* a, std::string* b) {
- ++test_count_;
- result_.append(*a);
- result_.append(*b);
- }
-
- void Test2Mixed(const std::string& a, std::string* b) {
- ++test_count_;
- result_.append(a);
- result_.append(*b);
- }
-
int test_count() const { return test_count_; }
const std::string& result() const { return result_; }
@@ -167,120 +143,6 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() {
EXPECT_TRUE(run_time.is_null());
}
-LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
- ADD_FAILURE() << "bad exception handler";
- ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
- return EXCEPTION_EXECUTE_HANDLER;
-}
-
-// This task throws an SEH exception: initially write to an invalid address.
-// If the right SEH filter is installed, it will fix the error.
-class Crasher : public RefCounted<Crasher> {
- public:
- // Ctor. If trash_SEH_handler is true, the task will override the unhandled
- // exception handler with one sure to crash this test.
- explicit Crasher(bool trash_SEH_handler)
- : trash_SEH_handler_(trash_SEH_handler) {
- }
-
- void Run() {
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(1));
- if (trash_SEH_handler_)
- ::SetUnhandledExceptionFilter(&BadExceptionHandler);
- // Generate a SEH fault. We do it in asm to make sure we know how to undo
- // the damage.
-
-#if defined(_M_IX86)
-
- __asm {
- mov eax, dword ptr [Crasher::bad_array_]
- mov byte ptr [eax], 66
- }
-
-#elif defined(_M_X64)
-
- bad_array_[0] = 66;
-
-#else
-#error "needs architecture support"
-#endif
-
- MessageLoop::current()->QuitWhenIdle();
- }
- // Points the bad array to a valid memory location.
- static void FixError() {
- bad_array_ = &valid_store_;
- }
-
- private:
- bool trash_SEH_handler_;
- static volatile char* bad_array_;
- static char valid_store_;
-};
-
-volatile char* Crasher::bad_array_ = 0;
-char Crasher::valid_store_ = 0;
-
-// This SEH filter fixes the problem and retries execution. Fixing requires
-// that the last instruction: mov eax, [Crasher::bad_array_] to be retried
-// so we move the instruction pointer 5 bytes back.
-LONG WINAPI HandleCrasherException(EXCEPTION_POINTERS *ex_info) {
- if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
- return EXCEPTION_EXECUTE_HANDLER;
-
- Crasher::FixError();
-
-#if defined(_M_IX86)
-
- ex_info->ContextRecord->Eip -= 5;
-
-#elif defined(_M_X64)
-
- ex_info->ContextRecord->Rip -= 5;
-
-#endif
-
- return EXCEPTION_CONTINUE_EXECUTION;
-}
-
-void RunTest_Crasher(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- if (::IsDebuggerPresent())
- return;
-
- LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
- ::SetUnhandledExceptionFilter(&HandleCrasherException);
-
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&Crasher::Run, new Crasher(false)));
- MessageLoop::current()->set_exception_restoration(true);
- MessageLoop::current()->Run();
- MessageLoop::current()->set_exception_restoration(false);
-
- ::SetUnhandledExceptionFilter(old_SEH_filter);
-}
-
-void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- if (::IsDebuggerPresent())
- return;
-
- LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
- ::SetUnhandledExceptionFilter(&HandleCrasherException);
-
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&Crasher::Run, new Crasher(true)));
- MessageLoop::current()->set_exception_restoration(true);
- MessageLoop::current()->Run();
- MessageLoop::current()->set_exception_restoration(false);
-
- ::SetUnhandledExceptionFilter(old_SEH_filter);
-}
-
const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test";
enum TaskType {
@@ -559,11 +421,11 @@ void PostNTasksThenQuit(int posts_remaining) {
#if defined(OS_WIN)
-class DispatcherImpl : public MessageLoopForUI::Dispatcher {
+class DispatcherImpl : public MessagePumpDispatcher {
public:
DispatcherImpl() : dispatch_count_(0) {}
- virtual bool Dispatch(const NativeEvent& msg) OVERRIDE {
+ virtual uint32_t Dispatch(const NativeEvent& msg) OVERRIDE {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
// Do not count WM_TIMER since it is not what we post and it will cause
@@ -571,7 +433,8 @@ class DispatcherImpl : public MessageLoopForUI::Dispatcher {
if (msg.message != WM_TIMER)
++dispatch_count_;
// We treat WM_LBUTTONUP as the last message.
- return msg.message != WM_LBUTTONUP;
+ return msg.message == WM_LBUTTONUP ? POST_DISPATCH_QUIT_LOOP
+ : POST_DISPATCH_NONE;
}
int dispatch_count_;
@@ -781,18 +644,6 @@ TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
RunTest_PostDelayedTask_SharedTimer_SubPump();
}
-TEST(MessageLoopTest, Crasher) {
- RunTest_Crasher(MessageLoop::TYPE_DEFAULT);
- RunTest_Crasher(MessageLoop::TYPE_UI);
- RunTest_Crasher(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, CrasherNasty) {
- RunTest_CrasherNasty(MessageLoop::TYPE_DEFAULT);
- RunTest_CrasherNasty(MessageLoop::TYPE_UI);
- RunTest_CrasherNasty(MessageLoop::TYPE_IO);
-}
-
// This test occasionally hangs http://crbug.com/44567
TEST(MessageLoopTest, DISABLED_RecursiveDenial2) {
RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT);
diff --git a/chromium/base/message_loop/message_pump.cc b/chromium/base/message_loop/message_pump.cc
index 7ffc2b170de..3d85b9b5643 100644
--- a/chromium/base/message_loop/message_pump.cc
+++ b/chromium/base/message_loop/message_pump.cc
@@ -12,4 +12,7 @@ MessagePump::MessagePump() {
MessagePump::~MessagePump() {
}
+void MessagePump::SetTimerSlack(TimerSlack) {
+}
+
} // namespace base
diff --git a/chromium/base/message_loop/message_pump.h b/chromium/base/message_loop/message_pump.h
index 816bb3c3d39..a795f31b67c 100644
--- a/chromium/base/message_loop/message_pump.h
+++ b/chromium/base/message_loop/message_pump.h
@@ -7,6 +7,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/message_loop/timer_slack.h"
#include "base/threading/non_thread_safe.h"
namespace base {
@@ -134,6 +135,9 @@ class BASE_EXPORT MessagePump : public NonThreadSafe {
// cancelling any pending DoDelayedWork callback. This method may only be
// used on the thread that called Run.
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0;
+
+ // Sets the timer slack to the specified value.
+ virtual void SetTimerSlack(TimerSlack timer_slack);
};
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_android.cc b/chromium/base/message_loop/message_pump_android.cc
index e756fdd3f52..babd17b577e 100644
--- a/chromium/base/message_loop/message_pump_android.cc
+++ b/chromium/base/message_loop/message_pump_android.cc
@@ -21,7 +21,8 @@ using base::android::ScopedJavaLocalRef;
// ----------------------------------------------------------------------------
// This method can not move to anonymous namespace as it has been declared as
// 'static' in system_message_handler_jni.h.
-static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate) {
+static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate,
+ jlong delayed_scheduled_time_ticks) {
base::MessagePump::Delegate* delegate =
reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
DCHECK(delegate);
@@ -33,16 +34,42 @@ static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate) {
// that will be processed before calling here again.
bool did_work = delegate->DoWork();
- // This is the time when we need to do delayed work.
- base::TimeTicks delayed_work_time;
- did_work |= delegate->DoDelayedWork(&delayed_work_time);
-
- // Always call this if there is a delayed message waiting in the queue
- // since is at most one delayed message in the Java message handler, and this
- // function call may be the result of that message being handled.
- if (!delayed_work_time.is_null()) {
- Java_SystemMessageHandler_setDelayedTimer(env, obj,
- (delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp());
+ // In the java side, |SystemMessageHandler| keeps a single "delayed" message.
+ // It's an expensive operation to |removeMessage| there, so this is optimized
+ // to avoid those calls.
+ //
+ // At this stage, |next_delayed_work_time| can be:
+ // 1) The same as previously scheduled: nothing to be done, move along. This
+ // is the typical case, since this method is called for every single message.
+ //
+ // 2) Not previously scheduled: just post a new message in java.
+ //
+ // 3) Shorter than previously scheduled: far less common. In this case,
+ // |removeMessage| and post a new one.
+ //
+ // 4) Longer than previously scheduled (or null): nothing to be done, move
+ // along.
+ //
+ // Side note: base::TimeTicks is a C++ representation and can't be
+ // compared in java. When calling |scheduleDelayedWork|, pass the
+ // |InternalValue()| to java and then back to C++ so the comparisons can be
+ // done here.
+ // This roundtrip allows comparing TimeTicks directly (cheap) and
+ // avoid comparisons with TimeDelta / Now() (expensive).
+ base::TimeTicks next_delayed_work_time;
+ did_work |= delegate->DoDelayedWork(&next_delayed_work_time);
+
+ if (!next_delayed_work_time.is_null()) {
+ // Schedule a new message if there's nothing already scheduled or there's a
+ // shorter delay than previously scheduled (see (2) and (3) above).
+ if (delayed_scheduled_time_ticks == 0 ||
+ next_delayed_work_time < base::TimeTicks::FromInternalValue(
+ delayed_scheduled_time_ticks)) {
+ Java_SystemMessageHandler_scheduleDelayedWork(env, obj,
+ next_delayed_work_time.ToInternalValue(),
+ (next_delayed_work_time -
+ base::TimeTicks::Now()).InMillisecondsRoundedUp());
+ }
}
// This is a major difference between android and other platforms: since we
@@ -90,7 +117,7 @@ void MessagePumpForUI::Quit() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
- Java_SystemMessageHandler_removeTimer(env,
+ Java_SystemMessageHandler_removeAllPendingMessages(env,
system_message_handler_obj_.obj());
system_message_handler_obj_.Reset();
}
@@ -108,7 +135,7 @@ void MessagePumpForUI::ScheduleWork() {
JNIEnv* env = base::android::AttachCurrentThread();
DCHECK(env);
- Java_SystemMessageHandler_setTimer(env,
+ Java_SystemMessageHandler_scheduleWork(env,
system_message_handler_obj_.obj());
}
@@ -122,8 +149,9 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
(delayed_work_time - TimeTicks::Now()).InMillisecondsRoundedUp();
// Note that we're truncating to milliseconds as required by the java side,
// even though delayed_work_time is microseconds resolution.
- Java_SystemMessageHandler_setDelayedTimer(env,
- system_message_handler_obj_.obj(), millis);
+ Java_SystemMessageHandler_scheduleDelayedWork(env,
+ system_message_handler_obj_.obj(),
+ delayed_work_time.ToInternalValue(), millis);
}
// static
diff --git a/chromium/base/message_loop/message_pump_dispatcher.h b/chromium/base/message_loop/message_pump_dispatcher.h
index e49fa4f15df..0dea22662f9 100644
--- a/chromium/base/message_loop/message_pump_dispatcher.h
+++ b/chromium/base/message_loop/message_pump_dispatcher.h
@@ -5,6 +5,8 @@
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DISPATCHER_H
+#include <stdint.h>
+
#include "base/base_export.h"
#include "base/event_types.h"
@@ -16,15 +18,24 @@ namespace base {
// every message is passed to Dispatcher's Dispatch method for dispatch. It is
// up to the Dispatcher whether or not to dispatch the event.
//
-// The nested loop is exited by either posting a quit, or returning false
-// from Dispatch.
+// The nested loop is exited by either posting a quit, or setting the
+// POST_DISPATCH_QUIT_LOOP flag on the return value from Dispatch.
class BASE_EXPORT MessagePumpDispatcher {
public:
+ enum PostDispatchAction {
+ POST_DISPATCH_NONE = 0x0,
+ POST_DISPATCH_QUIT_LOOP = 0x1,
+ POST_DISPATCH_PERFORM_DEFAULT = 0x2,
+ };
+
virtual ~MessagePumpDispatcher() {}
- // Dispatches the event. If true is returned processing continues as
- // normal. If false is returned, the nested loop exits immediately.
- virtual bool Dispatch(const NativeEvent& event) = 0;
+ // Dispatches the event. The return value can have more than one
+ // PostDispatchAction flags OR'ed together. If POST_DISPATCH_PERFORM_DEFAULT
+ // is set in the returned value, then the message-pump performs the default
+ // action. If POST_DISPATCH_QUIT_LOOP is set, in the return value, then the
+ // nested loop exits immediately.
+ virtual uint32_t Dispatch(const NativeEvent& event) = 0;
};
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_glib.cc b/chromium/base/message_loop/message_pump_glib.cc
index 13d8b2b9914..f06f60d8cf5 100644
--- a/chromium/base/message_loop/message_pump_glib.cc
+++ b/chromium/base/message_loop/message_pump_glib.cc
@@ -9,8 +9,10 @@
#include <glib.h>
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"
namespace base {
@@ -117,11 +119,53 @@ GSourceFuncs WorkSourceFuncs = {
NULL
};
+// The following is used to make sure we only run the MessagePumpGlib on one
+// thread. X only has one message pump so we can only have one UI loop per
+// process.
+#ifndef NDEBUG
+
+// Tracks the pump the most recent pump that has been run.
+struct ThreadInfo {
+ // The pump.
+ MessagePumpGlib* pump;
+
+ // ID of the thread the pump was run on.
+ PlatformThreadId thread_id;
+};
+
+// Used for accesing |thread_info|.
+static LazyInstance<Lock>::Leaky thread_info_lock = LAZY_INSTANCE_INITIALIZER;
+
+// If non-NULL it means a MessagePumpGlib exists and has been Run. This is
+// destroyed when the MessagePump is destroyed.
+ThreadInfo* thread_info = NULL;
+
+void CheckThread(MessagePumpGlib* pump) {
+ AutoLock auto_lock(thread_info_lock.Get());
+ if (!thread_info) {
+ thread_info = new ThreadInfo;
+ thread_info->pump = pump;
+ thread_info->thread_id = PlatformThread::CurrentId();
+ }
+ DCHECK(thread_info->thread_id == PlatformThread::CurrentId()) <<
+ "Running MessagePumpGlib on two different threads; "
+ "this is unsupported by GLib!";
+}
+
+void PumpDestroyed(MessagePumpGlib* pump) {
+ AutoLock auto_lock(thread_info_lock.Get());
+ if (thread_info && thread_info->pump == pump) {
+ delete thread_info;
+ thread_info = NULL;
+ }
+}
+
+#endif
+
} // namespace
struct MessagePumpGlib::RunState {
Delegate* delegate;
- MessagePumpDispatcher* dispatcher;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;
@@ -161,71 +205,15 @@ MessagePumpGlib::MessagePumpGlib()
}
MessagePumpGlib::~MessagePumpGlib() {
+#ifndef NDEBUG
+ PumpDestroyed(this);
+#endif
g_source_destroy(work_source_);
g_source_unref(work_source_);
close(wakeup_pipe_read_);
close(wakeup_pipe_write_);
}
-void MessagePumpGlib::RunWithDispatcher(Delegate* delegate,
- MessagePumpDispatcher* dispatcher) {
-#ifndef NDEBUG
- // Make sure we only run this on one thread. X/GTK only has one message pump
- // so we can only have one UI loop per process.
- static PlatformThreadId thread_id = PlatformThread::CurrentId();
- DCHECK(thread_id == PlatformThread::CurrentId()) <<
- "Running MessagePumpGlib on two different threads; "
- "this is unsupported by GLib!";
-#endif
-
- RunState state;
- state.delegate = delegate;
- state.dispatcher = dispatcher;
- state.should_quit = false;
- state.run_depth = state_ ? state_->run_depth + 1 : 1;
- state.has_work = false;
-
- RunState* previous_state = state_;
- state_ = &state;
-
- // We really only do a single task for each iteration of the loop. If we
- // have done something, assume there is likely something more to do. This
- // will mean that we don't block on the message pump until there was nothing
- // more to do. We also set this to true to make sure not to block on the
- // first iteration of the loop, so RunUntilIdle() works correctly.
- bool more_work_is_plausible = true;
-
- // We run our own loop instead of using g_main_loop_quit in one of the
- // callbacks. This is so we only quit our own loops, and we don't quit
- // nested loops run by others. TODO(deanm): Is this what we want?
- for (;;) {
- // Don't block if we think we have more work to do.
- bool block = !more_work_is_plausible;
-
- more_work_is_plausible = g_main_context_iteration(context_, block);
- if (state_->should_quit)
- break;
-
- more_work_is_plausible |= state_->delegate->DoWork();
- if (state_->should_quit)
- break;
-
- more_work_is_plausible |=
- state_->delegate->DoDelayedWork(&delayed_work_time_);
- if (state_->should_quit)
- break;
-
- if (more_work_is_plausible)
- continue;
-
- more_work_is_plausible = state_->delegate->DoIdleWork();
- if (state_->should_quit)
- break;
- }
-
- state_ = previous_state;
-}
-
// Return the timeout we want passed to poll.
int MessagePumpGlib::HandlePrepare() {
// We know we have work, but we haven't called HandleDispatch yet. Don't let
@@ -291,7 +279,55 @@ void MessagePumpGlib::HandleDispatch() {
}
void MessagePumpGlib::Run(Delegate* delegate) {
- RunWithDispatcher(delegate, NULL);
+#ifndef NDEBUG
+ CheckThread(this);
+#endif
+
+ RunState state;
+ state.delegate = delegate;
+ state.should_quit = false;
+ state.run_depth = state_ ? state_->run_depth + 1 : 1;
+ state.has_work = false;
+
+ RunState* previous_state = state_;
+ state_ = &state;
+
+ // We really only do a single task for each iteration of the loop. If we
+ // have done something, assume there is likely something more to do. This
+ // will mean that we don't block on the message pump until there was nothing
+ // more to do. We also set this to true to make sure not to block on the
+ // first iteration of the loop, so RunUntilIdle() works correctly.
+ bool more_work_is_plausible = true;
+
+ // We run our own loop instead of using g_main_loop_quit in one of the
+ // callbacks. This is so we only quit our own loops, and we don't quit
+ // nested loops run by others. TODO(deanm): Is this what we want?
+ for (;;) {
+ // Don't block if we think we have more work to do.
+ bool block = !more_work_is_plausible;
+
+ more_work_is_plausible = g_main_context_iteration(context_, block);
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |= state_->delegate->DoWork();
+ if (state_->should_quit)
+ break;
+
+ more_work_is_plausible |=
+ state_->delegate->DoDelayedWork(&delayed_work_time_);
+ if (state_->should_quit)
+ break;
+
+ if (more_work_is_plausible)
+ continue;
+
+ more_work_is_plausible = state_->delegate->DoIdleWork();
+ if (state_->should_quit)
+ break;
+ }
+
+ state_ = previous_state;
}
void MessagePumpGlib::Quit() {
@@ -319,8 +355,9 @@ void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
ScheduleWork();
}
-MessagePumpDispatcher* MessagePumpGlib::GetDispatcher() {
- return state_ ? state_->dispatcher : NULL;
+bool MessagePumpGlib::ShouldQuit() const {
+ CHECK(state_);
+ return state_->should_quit;
}
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_glib.h b/chromium/base/message_loop/message_pump_glib.h
index 174bd051f57..a13493a739c 100644
--- a/chromium/base/message_loop/message_pump_glib.h
+++ b/chromium/base/message_loop/message_pump_glib.h
@@ -17,19 +17,6 @@ typedef struct _GSource GSource;
namespace base {
-// MessagePumpObserver is notified prior to an event being dispatched. As
-// Observers are notified of every change, they have to be FAST! The platform
-// specific implementation of the class is in message_pump_gtk/message_pump_x.
-class MessagePumpObserver;
-
-// MessagePumpDispatcher is used during a nested invocation of Run to dispatch
-// events. If Run is invoked with a non-NULL MessagePumpDispatcher, MessageLoop
-// does not dispatch events (or invoke gtk_main_do_event), rather every event is
-// passed to Dispatcher's Dispatch method for dispatch. It is up to the
-// Dispatcher to dispatch, or not, the event. The platform specific
-// implementation of the class is in message_pump_gtk/message_pump_x.
-class MessagePumpDispatcher;
-
// This class implements a base MessagePump needed for TYPE_UI MessageLoops on
// platforms using GLib.
class BASE_EXPORT MessagePumpGlib : public MessagePump {
@@ -37,10 +24,6 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump {
MessagePumpGlib();
virtual ~MessagePumpGlib();
- // Like MessagePump::Run, but events are routed through dispatcher.
- virtual void RunWithDispatcher(Delegate* delegate,
- MessagePumpDispatcher* dispatcher);
-
// Internal methods used for processing the pump callbacks. They are
// public for simplicity but should not be used directly. HandlePrepare
// is called during the prepare step of glib, and returns a timeout that
@@ -57,11 +40,9 @@ class BASE_EXPORT MessagePumpGlib : public MessagePump {
virtual void ScheduleWork() OVERRIDE;
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
- protected:
- // Returns the dispatcher for the current run state (|state_->dispatcher|).
- MessagePumpDispatcher* GetDispatcher();
-
private:
+ bool ShouldQuit() const;
+
// We may make recursive calls to Run, so we save state that needs to be
// separate between them in this structure type.
struct RunState;
diff --git a/chromium/base/message_loop/message_pump_glib_unittest.cc b/chromium/base/message_loop/message_pump_glib_unittest.cc
index 033e6cde92d..aaf6b4d5d06 100644
--- a/chromium/base/message_loop/message_pump_glib_unittest.cc
+++ b/chromium/base/message_loop/message_pump_glib_unittest.cc
@@ -19,10 +19,6 @@
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
-#if defined(TOOLKIT_GTK)
-#include <gtk/gtk.h>
-#endif
-
namespace base {
namespace {
@@ -406,42 +402,6 @@ TEST_F(MessagePumpGLibTest, TestDrainingGLib) {
EXPECT_EQ(3, injector()->processed_events());
}
-
-namespace {
-
-#if defined(TOOLKIT_GTK)
-void AddEventsAndDrainGtk(EventInjector* injector) {
- // Add a couple of dummy events
- injector->AddDummyEvent(0);
- injector->AddDummyEvent(0);
- // Then add an event that will quit the main loop.
- injector->AddEvent(0, MessageLoop::QuitWhenIdleClosure());
-
- // Post a couple of dummy tasks
- MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
- MessageLoop::current()->PostTask(FROM_HERE, Bind(&DoNothing));
-
- // Drain the events
- while (gtk_events_pending()) {
- gtk_main_iteration();
- }
-}
-#endif
-
-} // namespace
-
-#if defined(TOOLKIT_GTK)
-TEST_F(MessagePumpGLibTest, TestDrainingGtk) {
- // Tests that draining events using Gtk works.
- loop()->PostTask(
- FROM_HERE,
- Bind(&AddEventsAndDrainGtk, Unretained(injector())));
- loop()->Run();
-
- EXPECT_EQ(3, injector()->processed_events());
-}
-#endif
-
namespace {
// Helper class that lets us run the GLib message loop.
@@ -456,15 +416,9 @@ class GLibLoopRunner : public RefCounted<GLibLoopRunner> {
}
void RunLoop() {
-#if defined(TOOLKIT_GTK)
- while (!quit_) {
- gtk_main_iteration();
- }
-#else
while (!quit_) {
g_main_context_iteration(NULL, TRUE);
}
-#endif
}
void Quit() {
diff --git a/chromium/base/message_loop/message_pump_gtk.cc b/chromium/base/message_loop/message_pump_gtk.cc
deleted file mode 100644
index 86d2415a412..00000000000
--- a/chromium/base/message_loop/message_pump_gtk.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/message_loop/message_pump_gtk.h"
-
-#include <gtk/gtk.h>
-#include <gdk/gdkx.h>
-
-#include "base/debug/trace_event.h"
-#include "base/profiler/scoped_profile.h"
-
-namespace base {
-
-namespace {
-
-const char* EventToTypeString(const GdkEvent* event) {
- switch (event->type) {
- case GDK_NOTHING: return "GDK_NOTHING";
- case GDK_DELETE: return "GDK_DELETE";
- case GDK_DESTROY: return "GDK_DESTROY";
- case GDK_EXPOSE: return "GDK_EXPOSE";
- case GDK_MOTION_NOTIFY: return "GDK_MOTION_NOTIFY";
- case GDK_BUTTON_PRESS: return "GDK_BUTTON_PRESS";
- case GDK_2BUTTON_PRESS: return "GDK_2BUTTON_PRESS";
- case GDK_3BUTTON_PRESS: return "GDK_3BUTTON_PRESS";
- case GDK_BUTTON_RELEASE: return "GDK_BUTTON_RELEASE";
- case GDK_KEY_PRESS: return "GDK_KEY_PRESS";
- case GDK_KEY_RELEASE: return "GDK_KEY_RELEASE";
- case GDK_ENTER_NOTIFY: return "GDK_ENTER_NOTIFY";
- case GDK_LEAVE_NOTIFY: return "GDK_LEAVE_NOTIFY";
- case GDK_FOCUS_CHANGE: return "GDK_FOCUS_CHANGE";
- case GDK_CONFIGURE: return "GDK_CONFIGURE";
- case GDK_MAP: return "GDK_MAP";
- case GDK_UNMAP: return "GDK_UNMAP";
- case GDK_PROPERTY_NOTIFY: return "GDK_PROPERTY_NOTIFY";
- case GDK_SELECTION_CLEAR: return "GDK_SELECTION_CLEAR";
- case GDK_SELECTION_REQUEST: return "GDK_SELECTION_REQUEST";
- case GDK_SELECTION_NOTIFY: return "GDK_SELECTION_NOTIFY";
- case GDK_PROXIMITY_IN: return "GDK_PROXIMITY_IN";
- case GDK_PROXIMITY_OUT: return "GDK_PROXIMITY_OUT";
- case GDK_DRAG_ENTER: return "GDK_DRAG_ENTER";
- case GDK_DRAG_LEAVE: return "GDK_DRAG_LEAVE";
- case GDK_DRAG_MOTION: return "GDK_DRAG_MOTION";
- case GDK_DRAG_STATUS: return "GDK_DRAG_STATUS";
- case GDK_DROP_START: return "GDK_DROP_START";
- case GDK_DROP_FINISHED: return "GDK_DROP_FINISHED";
- case GDK_CLIENT_EVENT: return "GDK_CLIENT_EVENT";
- case GDK_VISIBILITY_NOTIFY: return "GDK_VISIBILITY_NOTIFY";
- case GDK_NO_EXPOSE: return "GDK_NO_EXPOSE";
- case GDK_SCROLL: return "GDK_SCROLL";
- case GDK_WINDOW_STATE: return "GDK_WINDOW_STATE";
- case GDK_SETTING: return "GDK_SETTING";
- case GDK_OWNER_CHANGE: return "GDK_OWNER_CHANGE";
- case GDK_GRAB_BROKEN: return "GDK_GRAB_BROKEN";
- case GDK_DAMAGE: return "GDK_DAMAGE";
- default:
- return "Unknown Gdk Event";
- }
-}
-
-} // namespace
-
-MessagePumpGtk::MessagePumpGtk() : MessagePumpGlib() {
- gdk_event_handler_set(&EventDispatcher, this, NULL);
-}
-
-MessagePumpGtk::~MessagePumpGtk() {
- gdk_event_handler_set(reinterpret_cast<GdkEventFunc>(gtk_main_do_event),
- this, NULL);
-}
-
-void MessagePumpGtk::DispatchEvents(GdkEvent* event) {
- UNSHIPPED_TRACE_EVENT1("task", "MessagePumpGtk::DispatchEvents",
- "type", EventToTypeString(event));
-
- WillProcessEvent(event);
- gtk_main_do_event(event);
- DidProcessEvent(event);
-}
-
-// static
-Display* MessagePumpGtk::GetDefaultXDisplay() {
- static GdkDisplay* display = gdk_display_get_default();
- if (!display) {
- // GTK / GDK has not been initialized, which is a decision we wish to
- // support, for example for the GPU process.
- static Display* xdisplay = XOpenDisplay(NULL);
- return xdisplay;
- }
- return GDK_DISPLAY_XDISPLAY(display);
-}
-
-void MessagePumpGtk::AddObserver(MessagePumpGdkObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void MessagePumpGtk::RemoveObserver(MessagePumpGdkObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
-void MessagePumpGtk::WillProcessEvent(GdkEvent* event) {
- FOR_EACH_OBSERVER(MessagePumpGdkObserver,
- observers_,
- WillProcessEvent(event));
-}
-
-void MessagePumpGtk::DidProcessEvent(GdkEvent* event) {
- FOR_EACH_OBSERVER(MessagePumpGdkObserver,
- observers_,
- DidProcessEvent(event));
-}
-
-// static
-void MessagePumpGtk::EventDispatcher(GdkEvent* event, gpointer data) {
- MessagePumpGtk* message_pump = reinterpret_cast<MessagePumpGtk*>(data);
- message_pump->DispatchEvents(event);
-}
-
-} // namespace base
diff --git a/chromium/base/message_loop/message_pump_gtk.h b/chromium/base/message_loop/message_pump_gtk.h
deleted file mode 100644
index b3c3b8c7415..00000000000
--- a/chromium/base/message_loop/message_pump_gtk.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_
-
-#include "base/message_loop/message_pump_glib.h"
-
-typedef union _GdkEvent GdkEvent;
-typedef struct _XDisplay Display;
-
-namespace base {
-
-// The documentation for this class is in message_pump_glib.h
-class MessagePumpGdkObserver {
- public:
- // This method is called before processing a message.
- virtual void WillProcessEvent(GdkEvent* event) = 0;
-
- // This method is called after processing a message.
- virtual void DidProcessEvent(GdkEvent* event) = 0;
-
- protected:
- virtual ~MessagePumpGdkObserver() {}
-};
-
-// This class implements a message-pump for dispatching GTK events.
-class BASE_EXPORT MessagePumpGtk : public MessagePumpGlib {
- public:
- MessagePumpGtk();
- virtual ~MessagePumpGtk();
-
- // Dispatch an available GdkEvent. Essentially this allows a subclass to do
- // some task before/after calling the default handler (EventDispatcher).
- void DispatchEvents(GdkEvent* event);
-
- // Returns default X Display.
- static Display* GetDefaultXDisplay();
-
- // Adds an Observer, which will start receiving notifications immediately.
- void AddObserver(MessagePumpGdkObserver* observer);
-
- // Removes an Observer. It is safe to call this method while an Observer is
- // receiving a notification callback.
- void RemoveObserver(MessagePumpGdkObserver* observer);
-
- private:
- // Invoked from EventDispatcher. Notifies all observers we're about to
- // process an event.
- void WillProcessEvent(GdkEvent* event);
-
- // Invoked from EventDispatcher. Notifies all observers we processed an
- // event.
- void DidProcessEvent(GdkEvent* event);
-
- // Callback prior to gdk dispatching an event.
- static void EventDispatcher(GdkEvent* event, void* data);
-
- // List of observers.
- ObserverList<MessagePumpGdkObserver> observers_;
-
- DISALLOW_COPY_AND_ASSIGN(MessagePumpGtk);
-};
-
-typedef MessagePumpGtk MessagePumpForUI;
-
-} // namespace base
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GTK_H_
diff --git a/chromium/base/message_loop/message_pump_io_ios_unittest.cc b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
index f3b598c6ba6..e6dcc33a4bf 100644
--- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc
+++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
@@ -82,7 +82,7 @@ TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) {
ASSERT_DEBUG_DEATH(io_loop()->WatchFileDescriptor(
STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
"Check failed: "
- "watch_file_descriptor_caller_checker_.CalledOnValidThread()");
+ "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)");
}
#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc
index 26be687c6dd..d52025a1990 100644
--- a/chromium/base/message_loop/message_pump_libevent.cc
+++ b/chromium/base/message_loop/message_pump_libevent.cc
@@ -217,7 +217,7 @@ static void timer_callback(int fd, short events, void *context)
// Reentrant!
void MessagePumpLibevent::Run(Delegate* delegate) {
- DCHECK(keep_running_) << "Quit must have been called outside of Run!";
+ AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
AutoReset<bool> auto_reset_in_run(&in_run_, true);
// event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641.
@@ -275,12 +275,10 @@ void MessagePumpLibevent::Run(Delegate* delegate) {
}
}
}
-
- keep_running_ = true;
}
void MessagePumpLibevent::Quit() {
- DCHECK(in_run_);
+ DCHECK(in_run_) << "Quit was called outside of Run!";
// Tell both libevent and Run that they should break out of their loops.
keep_running_ = false;
ScheduleWork();
diff --git a/chromium/base/message_loop/message_pump_libevent_unittest.cc b/chromium/base/message_loop/message_pump_libevent_unittest.cc
index bf6d21c3280..9298d687609 100644
--- a/chromium/base/message_loop/message_pump_libevent_unittest.cc
+++ b/chromium/base/message_loop/message_pump_libevent_unittest.cc
@@ -6,8 +6,10 @@
#include <unistd.h>
+#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/posix/eintr_wrapper.h"
+#include "base/run_loop.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/libevent/event.h"
@@ -78,7 +80,13 @@ TEST_F(MessagePumpLibeventTest, TestWatchingFromBadThread) {
ASSERT_DEATH(io_loop()->WatchFileDescriptor(
STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
"Check failed: "
- "watch_file_descriptor_caller_checker_.CalledOnValidThread()");
+ "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)");
+}
+
+TEST_F(MessagePumpLibeventTest, QuitOutsideOfRun) {
+ scoped_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+ ASSERT_DEATH(pump->Quit(), "Check failed: in_run_. "
+ "Quit was called outside of Run!");
}
#endif // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
@@ -157,6 +165,41 @@ TEST_F(MessagePumpLibeventTest, StopWatcher) {
OnLibeventNotification(pump.get(), &watcher);
}
+void QuitMessageLoopAndStart(const Closure& quit_closure) {
+ quit_closure.Run();
+
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ RunLoop runloop;
+ MessageLoop::current()->PostTask(FROM_HERE, runloop.QuitClosure());
+ runloop.Run();
+}
+
+class NestedPumpWatcher : public MessagePumpLibevent::Watcher {
+ public:
+ NestedPumpWatcher() {}
+ virtual ~NestedPumpWatcher() {}
+
+ virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {
+ RunLoop runloop;
+ MessageLoop::current()->PostTask(FROM_HERE, Bind(&QuitMessageLoopAndStart,
+ runloop.QuitClosure()));
+ runloop.Run();
+ }
+
+ virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {}
+};
+
+TEST_F(MessagePumpLibeventTest, NestedPumpWatcher) {
+ scoped_ptr<MessagePumpLibevent> pump(new MessagePumpLibevent);
+ MessagePumpLibevent::FileDescriptorWatcher watcher;
+ NestedPumpWatcher delegate;
+ pump->WatchFileDescriptor(pipefds_[1],
+ false, MessagePumpLibevent::WATCH_READ, &watcher, &delegate);
+
+ // Spoof a libevent notification.
+ OnLibeventNotification(pump.get(), &watcher);
+}
+
} // namespace
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_mac.h b/chromium/base/message_loop/message_pump_mac.h
index 424cb70938d..6e63d2b5516 100644
--- a/chromium/base/message_loop/message_pump_mac.h
+++ b/chromium/base/message_loop/message_pump_mac.h
@@ -37,10 +37,9 @@
#include <CoreFoundation/CoreFoundation.h>
#include "base/memory/weak_ptr.h"
+#include "base/message_loop/timer_slack.h"
-#if !defined(__OBJC__)
-class NSAutoreleasePool;
-#else // !defined(__OBJC__)
+#if defined(__OBJC__)
#if defined(OS_IOS)
#import <Foundation/Foundation.h>
#else
@@ -55,7 +54,7 @@ class NSAutoreleasePool;
- (BOOL)isHandlingSendEvent;
@end
#endif // !defined(OS_IOS)
-#endif // !defined(__OBJC__)
+#endif // defined(__OBJC__)
namespace base {
@@ -63,6 +62,22 @@ class MessagePumpInstrumentation;
class RunLoop;
class TimeTicks;
+// AutoreleasePoolType is a proxy type for autorelease pools. Its definition
+// depends on the translation unit (TU) in which this header appears. In pure
+// C++ TUs, it is defined as a forward C++ class declaration (that is never
+// defined), because autorelease pools are an Objective-C concept. In Automatic
+// Reference Counting (ARC) Objective-C TUs, it is similarly defined as a
+// forward C++ class declaration, because clang will not allow the type
+// "NSAutoreleasePool" in such TUs. Finally, in Manual Retain Release (MRR)
+// Objective-C TUs, it is a type alias for NSAutoreleasePool. In all cases, a
+// method that takes or returns an NSAutoreleasePool* can use
+// AutoreleasePoolType* instead.
+#if !defined(__OBJC__) || __has_feature(objc_arc)
+class AutoreleasePoolType;
+#else // !defined(__OBJC__) || __has_feature(objc_arc)
+typedef NSAutoreleasePool AutoreleasePoolType;
+#endif // !defined(__OBJC__) || __has_feature(objc_arc)
+
class MessagePumpCFRunLoopBase : public MessagePump {
// Needs access to CreateAutoreleasePool.
friend class MessagePumpScopedAutoreleasePool;
@@ -79,6 +94,7 @@ class MessagePumpCFRunLoopBase : public MessagePump {
virtual void ScheduleWork() OVERRIDE;
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) OVERRIDE;
+ virtual void SetTimerSlack(TimerSlack timer_slack) OVERRIDE;
protected:
// Accessors for private data members to be used by subclasses.
@@ -94,7 +110,7 @@ class MessagePumpCFRunLoopBase : public MessagePump {
// In some cases, CreateAutoreleasePool may return nil intentionally to
// preventing an autorelease pool from being created, allowing any
// objects autoreleased by work to fall into the current autorelease pool.
- virtual NSAutoreleasePool* CreateAutoreleasePool();
+ virtual AutoreleasePoolType* CreateAutoreleasePool();
// Enables instrumentation of the MessagePump. See MessagePumpInstrumentation
// in the implementation for details.
@@ -181,6 +197,8 @@ class MessagePumpCFRunLoopBase : public MessagePump {
// See PowerStateNotification.
CFAbsoluteTime delayed_work_fire_time_;
+ base::TimerSlack timer_slack_;
+
// The recursion depth of the currently-executing CFRunLoopRun loop on the
// run loop's thread. 0 if no run loops are running inside of whatever scope
// the object was created in.
@@ -205,7 +223,7 @@ class MessagePumpCFRunLoopBase : public MessagePump {
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase);
};
-class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
+class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
public:
MessagePumpCFRunLoop();
virtual ~MessagePumpCFRunLoop();
@@ -224,9 +242,9 @@ class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop);
};
-class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
+class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
public:
- BASE_EXPORT MessagePumpNSRunLoop();
+ MessagePumpNSRunLoop();
virtual ~MessagePumpNSRunLoop();
virtual void DoRun(Delegate* delegate) OVERRIDE;
@@ -296,14 +314,14 @@ class MessagePumpCrApplication : public MessagePumpNSApplication {
protected:
// Returns nil if NSApp is currently in the middle of calling
// -sendEvent. Requires NSApp implementing CrAppProtocol.
- virtual NSAutoreleasePool* CreateAutoreleasePool() OVERRIDE;
+ virtual AutoreleasePoolType* CreateAutoreleasePool() OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication);
};
#endif // !defined(OS_IOS)
-class MessagePumpMac {
+class BASE_EXPORT MessagePumpMac {
public:
// If not on the main thread, returns a new instance of
// MessagePumpNSRunLoop.
@@ -321,17 +339,21 @@ class MessagePumpMac {
// UsingCrApp() returns false if the message pump was created before
// NSApp was initialized, or if NSApp does not implement
// CrAppProtocol. NSApp must be initialized before calling.
- BASE_EXPORT static bool UsingCrApp();
+ static bool UsingCrApp();
// Wrapper to query -[NSApp isHandlingSendEvent] from C++ code.
// Requires NSApp to implement CrAppProtocol.
- BASE_EXPORT static bool IsHandlingSendEvent();
+ static bool IsHandlingSendEvent();
#endif // !defined(OS_IOS)
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac);
};
+// Tasks posted to the message loop are posted under this mode, as well
+// as kCFRunLoopCommonModes.
+extern const CFStringRef BASE_EXPORT kMessageLoopExclusiveRunLoopMode;
+
} // namespace base
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
diff --git a/chromium/base/message_loop/message_pump_mac.mm b/chromium/base/message_loop/message_pump_mac.mm
index 9a8e95a6e19..0ab9ab7c467 100644
--- a/chromium/base/message_loop/message_pump_mac.mm
+++ b/chromium/base/message_loop/message_pump_mac.mm
@@ -4,6 +4,7 @@
#import "base/message_loop/message_pump_mac.h"
+#include <dlfcn.h>
#import <Foundation/Foundation.h>
#include <limits>
@@ -12,6 +13,7 @@
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/message_loop/timer_slack.h"
#include "base/metrics/histogram.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
@@ -21,8 +23,44 @@
#import <AppKit/AppKit.h>
#endif // !defined(OS_IOS)
+namespace base {
+
namespace {
+void CFRunLoopAddSourceToAllModes(CFRunLoopRef rl, CFRunLoopSourceRef source) {
+ CFRunLoopAddSource(rl, source, kCFRunLoopCommonModes);
+ CFRunLoopAddSource(rl, source, kMessageLoopExclusiveRunLoopMode);
+}
+
+void CFRunLoopRemoveSourceFromAllModes(CFRunLoopRef rl,
+ CFRunLoopSourceRef source) {
+ CFRunLoopRemoveSource(rl, source, kCFRunLoopCommonModes);
+ CFRunLoopRemoveSource(rl, source, kMessageLoopExclusiveRunLoopMode);
+}
+
+void CFRunLoopAddTimerToAllModes(CFRunLoopRef rl, CFRunLoopTimerRef timer) {
+ CFRunLoopAddTimer(rl, timer, kCFRunLoopCommonModes);
+ CFRunLoopAddTimer(rl, timer, kMessageLoopExclusiveRunLoopMode);
+}
+
+void CFRunLoopRemoveTimerFromAllModes(CFRunLoopRef rl,
+ CFRunLoopTimerRef timer) {
+ CFRunLoopRemoveTimer(rl, timer, kCFRunLoopCommonModes);
+ CFRunLoopRemoveTimer(rl, timer, kMessageLoopExclusiveRunLoopMode);
+}
+
+void CFRunLoopAddObserverToAllModes(CFRunLoopRef rl,
+ CFRunLoopObserverRef observer) {
+ CFRunLoopAddObserver(rl, observer, kCFRunLoopCommonModes);
+ CFRunLoopAddObserver(rl, observer, kMessageLoopExclusiveRunLoopMode);
+}
+
+void CFRunLoopRemoveObserverFromAllModes(CFRunLoopRef rl,
+ CFRunLoopObserverRef observer) {
+ CFRunLoopRemoveObserver(rl, observer, kCFRunLoopCommonModes);
+ CFRunLoopRemoveObserver(rl, observer, kMessageLoopExclusiveRunLoopMode);
+}
+
void NoOp(void* info) {
}
@@ -35,9 +73,38 @@ const CFTimeInterval kCFTimeIntervalMax =
bool g_not_using_cr_app = false;
#endif
+// Call through to CFRunLoopTimerSetTolerance(), which is only available on
+// OS X 10.9.
+void SetTimerTolerance(CFRunLoopTimerRef timer, CFTimeInterval tolerance) {
+ typedef void (*CFRunLoopTimerSetTolerancePtr)(CFRunLoopTimerRef timer,
+ CFTimeInterval tolerance);
+
+ static CFRunLoopTimerSetTolerancePtr settimertolerance_function_ptr;
+
+ static dispatch_once_t get_timer_tolerance_function_ptr_once;
+ dispatch_once(&get_timer_tolerance_function_ptr_once, ^{
+ NSBundle* bundle =[NSBundle
+ bundleWithPath:@"/System/Library/Frameworks/CoreFoundation.framework"];
+ const char* path = [[bundle executablePath] fileSystemRepresentation];
+ CHECK(path);
+ void* library_handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
+ CHECK(library_handle) << dlerror();
+ settimertolerance_function_ptr =
+ reinterpret_cast<CFRunLoopTimerSetTolerancePtr>(
+ dlsym(library_handle, "CFRunLoopTimerSetTolerance"));
+
+ dlclose(library_handle);
+ });
+
+ if (settimertolerance_function_ptr)
+ settimertolerance_function_ptr(timer, tolerance);
+}
+
} // namespace
-namespace base {
+// static
+const CFStringRef kMessageLoopExclusiveRunLoopMode =
+ CFSTR("kMessageLoopExclusiveRunLoopMode");
// A scoper for autorelease pools created from message pump run loops.
// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
@@ -95,9 +162,7 @@ class MessagePumpInstrumentation {
0, // order
&MessagePumpInstrumentation::TimerFired,
&timer_context));
- CFRunLoopAddTimer(CFRunLoopGetCurrent(),
- timer_,
- kCFRunLoopCommonModes);
+ CFRunLoopAddTimerToAllModes(CFRunLoopGetCurrent(), timer_);
}
// Used to track kCFRunLoopEntry.
@@ -282,6 +347,7 @@ class MessagePumpInstrumentation {
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
: delegate_(NULL),
delayed_work_fire_time_(kCFTimeIntervalMax),
+ timer_slack_(base::TIMER_SLACK_NONE),
nesting_level_(0),
run_nesting_level_(0),
deepest_nesting_level_(0),
@@ -302,7 +368,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
RunDelayedWorkTimer,
&timer_context);
- CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
+ CFRunLoopAddTimerToAllModes(run_loop_, delayed_work_timer_);
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
source_context.info = this;
@@ -310,20 +376,19 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
work_source_ = CFRunLoopSourceCreate(NULL, // allocator
1, // priority
&source_context);
- CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes);
+ CFRunLoopAddSourceToAllModes(run_loop_, work_source_);
source_context.perform = RunIdleWorkSource;
idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
2, // priority
&source_context);
- CFRunLoopAddSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
+ CFRunLoopAddSourceToAllModes(run_loop_, idle_work_source_);
source_context.perform = RunNestingDeferredWorkSource;
nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
- CFRunLoopAddSource(run_loop_, nesting_deferred_work_source_,
- kCFRunLoopCommonModes);
+ CFRunLoopAddSourceToAllModes(run_loop_, nesting_deferred_work_source_);
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
observer_context.info = this;
@@ -334,7 +399,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
StartOrEndWaitObserver,
&observer_context);
- CFRunLoopAddObserver(run_loop_, pre_wait_observer_, kCFRunLoopCommonModes);
+ CFRunLoopAddObserverToAllModes(run_loop_, pre_wait_observer_);
pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopBeforeSources,
@@ -342,7 +407,7 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
PreSourceObserver,
&observer_context);
- CFRunLoopAddObserver(run_loop_, pre_source_observer_, kCFRunLoopCommonModes);
+ CFRunLoopAddObserverToAllModes(run_loop_, pre_source_observer_);
enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
kCFRunLoopEntry |
@@ -351,36 +416,32 @@ MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase()
0, // priority
EnterExitObserver,
&observer_context);
- CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes);
+ CFRunLoopAddObserverToAllModes(run_loop_, enter_exit_observer_);
}
// Ideally called on the run loop thread. If other run loops were running
// lower on the run loop thread's stack when this object was created, the
// same number of run loops must be running when this object is destroyed.
MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
- CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_,
- kCFRunLoopCommonModes);
+ CFRunLoopRemoveObserverFromAllModes(run_loop_, enter_exit_observer_);
CFRelease(enter_exit_observer_);
- CFRunLoopRemoveObserver(run_loop_, pre_source_observer_,
- kCFRunLoopCommonModes);
+ CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_source_observer_);
CFRelease(pre_source_observer_);
- CFRunLoopRemoveObserver(run_loop_, pre_wait_observer_,
- kCFRunLoopCommonModes);
+ CFRunLoopRemoveObserverFromAllModes(run_loop_, pre_wait_observer_);
CFRelease(pre_wait_observer_);
- CFRunLoopRemoveSource(run_loop_, nesting_deferred_work_source_,
- kCFRunLoopCommonModes);
+ CFRunLoopRemoveSourceFromAllModes(run_loop_, nesting_deferred_work_source_);
CFRelease(nesting_deferred_work_source_);
- CFRunLoopRemoveSource(run_loop_, idle_work_source_, kCFRunLoopCommonModes);
+ CFRunLoopRemoveSourceFromAllModes(run_loop_, idle_work_source_);
CFRelease(idle_work_source_);
- CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes);
+ CFRunLoopRemoveSourceFromAllModes(run_loop_, work_source_);
CFRelease(work_source_);
- CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes);
+ CFRunLoopRemoveTimerFromAllModes(run_loop_, delayed_work_timer_);
CFRelease(delayed_work_timer_);
CFRelease(run_loop_);
@@ -438,6 +499,15 @@ void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
TimeDelta delta = delayed_work_time - TimeTicks::Now();
delayed_work_fire_time_ = CFAbsoluteTimeGetCurrent() + delta.InSecondsF();
CFRunLoopTimerSetNextFireDate(delayed_work_timer_, delayed_work_fire_time_);
+ if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
+ SetTimerTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
+ } else {
+ SetTimerTolerance(delayed_work_timer_, 0);
+ }
+}
+
+void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
+ timer_slack_ = timer_slack;
}
// Called from the run loop.
@@ -697,7 +767,7 @@ void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
}
// Base version returns a standard NSAutoreleasePool.
-NSAutoreleasePool* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
+AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
return [[NSAutoreleasePool alloc] init];
}
@@ -760,11 +830,11 @@ MessagePumpNSRunLoop::MessagePumpNSRunLoop()
quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
0, // priority
&source_context);
- CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
+ CFRunLoopAddSourceToAllModes(run_loop(), quit_source_);
}
MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
- CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
+ CFRunLoopRemoveSourceFromAllModes(run_loop(), quit_source_);
CFRelease(quit_source_);
}
@@ -909,7 +979,7 @@ MessagePumpCrApplication::~MessagePumpCrApplication() {
// CrApplication is responsible for setting handlingSendEvent to true just
// before it sends the event through the event handling mechanism, and
// returning it to its previous value once the event has been sent.
-NSAutoreleasePool* MessagePumpCrApplication::CreateAutoreleasePool() {
+AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() {
if (MessagePumpMac::IsHandlingSendEvent())
return nil;
return MessagePumpNSApplication::CreateAutoreleasePool();
diff --git a/chromium/base/message_loop/message_pump_observer.h b/chromium/base/message_loop/message_pump_observer.h
index 333a75f1cbb..d6e7abf786a 100644
--- a/chromium/base/message_loop/message_pump_observer.h
+++ b/chromium/base/message_loop/message_pump_observer.h
@@ -8,34 +8,22 @@
#include "base/base_export.h"
#include "base/event_types.h"
-namespace base {
-
-enum EventStatus {
- EVENT_CONTINUE, // The event should be dispatched as normal.
-#if defined(USE_X11)
- EVENT_HANDLED // The event should not be processed any farther.
+#if !defined(OS_WIN)
+#error Should not be here.
#endif
-};
+
+namespace base {
// A MessagePumpObserver is an object that receives global
-// notifications from the UI MessageLoop with MessagePumpWin or
-// MessagePumpX11.
+// notifications from the UI MessageLoop with MessagePumpWin.
//
// NOTE: An Observer implementation should be extremely fast!
-//
-// For use with MessagePumpX11, please see message_pump_glib.h for more
-// info about how this is invoked in this environment.
class BASE_EXPORT MessagePumpObserver {
public:
- // This method is called before processing a NativeEvent. If the
- // method returns EVENT_HANDLED, it indicates the event has already
- // been handled, so the event is not processed any farther. If the
- // method returns EVENT_CONTINUE, the event dispatching proceeds as
- // normal.
- virtual EventStatus WillProcessEvent(const NativeEvent& event) = 0;
-
- // This method is called after processing a message. This method
- // will not be called if WillProcessEvent returns EVENT_HANDLED.
+ // This method is called before processing a NativeEvent.
+ virtual void WillProcessEvent(const NativeEvent& event) = 0;
+
+ // This method is called after processing a message.
virtual void DidProcessEvent(const NativeEvent& event) = 0;
protected:
diff --git a/chromium/base/message_loop/message_pump_ozone.cc b/chromium/base/message_loop/message_pump_ozone.cc
deleted file mode 100644
index f7bff6d3ace..00000000000
--- a/chromium/base/message_loop/message_pump_ozone.cc
+++ /dev/null
@@ -1,61 +0,0 @@
-// 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.
-
-#include "base/message_loop/message_pump_ozone.h"
-
-#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-
-namespace base {
-
-MessagePumpOzone::MessagePumpOzone()
- : MessagePumpLibevent() {
-}
-
-MessagePumpOzone::~MessagePumpOzone() {
-}
-
-void MessagePumpOzone::AddObserver(MessagePumpObserver* /* observer */) {
- NOTIMPLEMENTED();
-}
-
-void MessagePumpOzone::RemoveObserver(MessagePumpObserver* /* observer */) {
- NOTIMPLEMENTED();
-}
-
-// static
-MessagePumpOzone* MessagePumpOzone::Current() {
- MessageLoopForUI* loop = MessageLoopForUI::current();
- return static_cast<MessagePumpOzone*>(loop->pump_ui());
-}
-
-void MessagePumpOzone::AddDispatcherForRootWindow(
- MessagePumpDispatcher* dispatcher) {
- // Only one root window is supported.
- DCHECK(dispatcher_.size() == 0);
- dispatcher_.insert(dispatcher_.begin(),dispatcher);
-}
-
-void MessagePumpOzone::RemoveDispatcherForRootWindow(
- MessagePumpDispatcher* dispatcher) {
- DCHECK(dispatcher_.size() == 1);
- dispatcher_.pop_back();
-}
-
-bool MessagePumpOzone::Dispatch(const NativeEvent& dev) {
- if (dispatcher_.size() > 0)
- return dispatcher_[0]->Dispatch(dev);
- else
- return true;
-}
-
-// This code assumes that the caller tracks the lifetime of the |dispatcher|.
-void MessagePumpOzone::RunWithDispatcher(
- Delegate* delegate, MessagePumpDispatcher* dispatcher) {
- dispatcher_.push_back(dispatcher);
- Run(delegate);
- dispatcher_.pop_back();
-}
-
-} // namespace base
diff --git a/chromium/base/message_loop/message_pump_ozone.h b/chromium/base/message_loop/message_pump_ozone.h
deleted file mode 100644
index edcdf2e3a85..00000000000
--- a/chromium/base/message_loop/message_pump_ozone.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// 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_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_
-#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_
-
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
-#include "base/message_loop/message_pump_dispatcher.h"
-#include "base/message_loop/message_pump_libevent.h"
-#include "base/message_loop/message_pump_observer.h"
-#include "base/observer_list.h"
-
-namespace base {
-
-// This class implements a message-pump for processing events from input devices
-// Refer to MessagePump for further documentation.
-class BASE_EXPORT MessagePumpOzone : public MessagePumpLibevent,
- public MessagePumpDispatcher {
- public:
- MessagePumpOzone();
- virtual ~MessagePumpOzone();
-
- // Returns the UI message pump.
- static MessagePumpOzone* Current();
-
- // Add/Remove the root window dispatcher.
- void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
- void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
-
- void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher);
-
- // Add / remove an Observer, which will start receiving notifications
- // immediately.
- void AddObserver(MessagePumpObserver* observer);
- void RemoveObserver(MessagePumpObserver* observer);
-
- // Overridden from MessagePumpDispatcher.
- virtual bool Dispatch(const NativeEvent& event) OVERRIDE;
-
- private:
- std::vector<MessagePumpDispatcher*> dispatcher_;
-
- DISALLOW_COPY_AND_ASSIGN(MessagePumpOzone);
-};
-
-typedef MessagePumpOzone MessagePumpForUI;
-
-} // namespace base
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_OZONE_H_
diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc
index 1927473b1eb..ae022bf0957 100644
--- a/chromium/base/message_loop/message_pump_win.cc
+++ b/chromium/base/message_loop/message_pump_win.cc
@@ -97,8 +97,7 @@ int MessagePumpWin::GetCurrentDelay() const {
// MessagePumpForUI public:
MessagePumpForUI::MessagePumpForUI()
- : atom_(0),
- message_filter_(new MessageFilter) {
+ : atom_(0) {
InitMessageWnd();
}
@@ -346,7 +345,7 @@ bool MessagePumpForUI::ProcessNextWindowsMessage() {
sent_messages_in_queue = true;
MSG msg;
- if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE)
return ProcessMessageHelper(msg);
return sent_messages_in_queue;
@@ -372,14 +371,14 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
WillProcessMessage(msg);
- if (!message_filter_->ProcessMessage(msg)) {
- if (state_->dispatcher) {
- if (!state_->dispatcher->Dispatch(msg))
- state_->should_quit = true;
- } else {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
+ uint32_t action = MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT;
+ if (state_->dispatcher)
+ action = state_->dispatcher->Dispatch(msg);
+ if (action & MessagePumpDispatcher::POST_DISPATCH_QUIT_LOOP)
+ state_->should_quit = true;
+ if (action & MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
}
DidProcessMessage(msg);
@@ -406,8 +405,7 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() {
have_message = PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE) ||
PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE);
} else {
- have_message = !!message_filter_->DoPeekMessage(&msg, NULL, 0, 0,
- PM_REMOVE);
+ have_message = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != FALSE;
}
DCHECK(!have_message || kMsgHaveWork != msg.message ||
@@ -429,11 +427,6 @@ bool MessagePumpForUI::ProcessPumpReplacementMessage() {
return ProcessMessageHelper(msg);
}
-void MessagePumpForUI::SetMessageFilter(
- scoped_ptr<MessageFilter> message_filter) {
- message_filter_ = message_filter.Pass();
-}
-
//-----------------------------------------------------------------------------
// MessagePumpForIO public:
diff --git a/chromium/base/message_loop/message_pump_win.h b/chromium/base/message_loop/message_pump_win.h
index 9184058a6c0..535a21320e0 100644
--- a/chromium/base/message_loop/message_pump_win.h
+++ b/chromium/base/message_loop/message_pump_win.h
@@ -11,7 +11,6 @@
#include "base/base_export.h"
#include "base/basictypes.h"
-#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_pump.h"
#include "base/message_loop/message_pump_dispatcher.h"
#include "base/message_loop/message_pump_observer.h"
@@ -127,44 +126,12 @@ class BASE_EXPORT MessagePumpWin : public MessagePump {
//
class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
public:
- // A MessageFilter implements the common Peek/Translate/Dispatch code to deal
- // with windows messages.
- // This abstraction is used to inject TSF message peeking. See
- // TextServicesMessageFilter.
- class BASE_EXPORT MessageFilter {
- public:
- virtual ~MessageFilter() {}
- // Implements the functionality exposed by the OS through PeekMessage.
- virtual BOOL DoPeekMessage(MSG* msg,
- HWND window_handle,
- UINT msg_filter_min,
- UINT msg_filter_max,
- UINT remove_msg) {
- return PeekMessage(msg, window_handle, msg_filter_min, msg_filter_max,
- remove_msg);
- }
- // Returns true if |message| was consumed by the filter and no extra
- // processing is required. If this method returns false, it is the
- // responsibility of the caller to ensure that normal processing takes
- // place.
- // The priority to consume messages is the following:
- // - Native Windows' message filter (CallMsgFilter).
- // - MessageFilter::ProcessMessage.
- // - MessagePumpDispatcher.
- // - TranslateMessage / DispatchMessage.
- virtual bool ProcessMessage(const MSG& msg) { return false;}
- };
// The application-defined code passed to the hook procedure.
static const int kMessageFilterCode = 0x5001;
MessagePumpForUI();
virtual ~MessagePumpForUI();
- // Sets a new MessageFilter. MessagePumpForUI takes ownership of
- // |message_filter|. When SetMessageFilter is called, old MessageFilter is
- // deleted.
- void SetMessageFilter(scoped_ptr<MessageFilter> message_filter);
-
// MessagePump methods:
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
@@ -188,8 +155,6 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
// A hidden message-only window.
HWND message_hwnd_;
-
- scoped_ptr<MessageFilter> message_filter_;
};
//-----------------------------------------------------------------------------
diff --git a/chromium/base/message_loop/message_pump_x11.cc b/chromium/base/message_loop/message_pump_x11.cc
deleted file mode 100644
index 35dcc040348..00000000000
--- a/chromium/base/message_loop/message_pump_x11.cc
+++ /dev/null
@@ -1,313 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/message_loop/message_pump_x11.h"
-
-#include <glib.h>
-#include <X11/X.h>
-#include <X11/extensions/XInput2.h>
-#include <X11/XKBlib.h>
-
-#include "base/basictypes.h"
-#include "base/message_loop/message_loop.h"
-
-namespace base {
-
-namespace {
-
-gboolean XSourcePrepare(GSource* source, gint* timeout_ms) {
- if (XPending(MessagePumpX11::GetDefaultXDisplay()))
- *timeout_ms = 0;
- else
- *timeout_ms = -1;
- return FALSE;
-}
-
-gboolean XSourceCheck(GSource* source) {
- return XPending(MessagePumpX11::GetDefaultXDisplay());
-}
-
-gboolean XSourceDispatch(GSource* source,
- GSourceFunc unused_func,
- gpointer data) {
- MessagePumpX11* pump = static_cast<MessagePumpX11*>(data);
- return pump->DispatchXEvents();
-}
-
-GSourceFuncs XSourceFuncs = {
- XSourcePrepare,
- XSourceCheck,
- XSourceDispatch,
- NULL
-};
-
-// The connection is essentially a global that's accessed through a static
-// method and destroyed whenever ~MessagePumpX11() is called. We do this
-// for historical reasons so user code can call
-// MessagePumpForUI::GetDefaultXDisplay() where MessagePumpForUI is a typedef
-// to whatever type in the current build.
-//
-// TODO(erg): This can be changed to something more sane like
-// MessagePumpX11::Current()->display() once MessagePumpGtk goes away.
-Display* g_xdisplay = NULL;
-int g_xinput_opcode = -1;
-
-bool InitializeXInput2() {
- Display* display = MessagePumpX11::GetDefaultXDisplay();
- if (!display)
- return false;
-
- int event, err;
-
- int xiopcode;
- if (!XQueryExtension(display, "XInputExtension", &xiopcode, &event, &err)) {
- DVLOG(1) << "X Input extension not available.";
- return false;
- }
- g_xinput_opcode = xiopcode;
-
-#if defined(USE_XI2_MT)
- // USE_XI2_MT also defines the required XI2 minor minimum version.
- int major = 2, minor = USE_XI2_MT;
-#else
- int major = 2, minor = 0;
-#endif
- if (XIQueryVersion(display, &major, &minor) == BadRequest) {
- DVLOG(1) << "XInput2 not supported in the server.";
- return false;
- }
-#if defined(USE_XI2_MT)
- if (major < 2 || (major == 2 && minor < USE_XI2_MT)) {
- DVLOG(1) << "XI version on server is " << major << "." << minor << ". "
- << "But 2." << USE_XI2_MT << " is required.";
- return false;
- }
-#endif
-
- return true;
-}
-
-Window FindEventTarget(const NativeEvent& xev) {
- Window target = xev->xany.window;
- if (xev->type == GenericEvent &&
- static_cast<XIEvent*>(xev->xcookie.data)->extension == g_xinput_opcode) {
- target = static_cast<XIDeviceEvent*>(xev->xcookie.data)->event;
- }
- return target;
-}
-
-bool InitializeXkb() {
- Display* display = MessagePumpX11::GetDefaultXDisplay();
- if (!display)
- return false;
-
- int opcode, event, error;
- int major = XkbMajorVersion;
- int minor = XkbMinorVersion;
- if (!XkbQueryExtension(display, &opcode, &event, &error, &major, &minor)) {
- DVLOG(1) << "Xkb extension not available.";
- return false;
- }
-
- // Ask the server not to send KeyRelease event when the user holds down a key.
- // crbug.com/138092
- Bool supported_return;
- if (!XkbSetDetectableAutoRepeat(display, True, &supported_return)) {
- DVLOG(1) << "XKB not supported in the server.";
- return false;
- }
-
- return true;
-}
-
-} // namespace
-
-MessagePumpX11::MessagePumpX11() : MessagePumpGlib(),
- x_source_(NULL) {
- InitializeXInput2();
- InitializeXkb();
- InitXSource();
-
- // Can't put this in the initializer list because g_xdisplay may not exist
- // until after InitXSource().
- x_root_window_ = DefaultRootWindow(g_xdisplay);
-}
-
-MessagePumpX11::~MessagePumpX11() {
- g_source_destroy(x_source_);
- g_source_unref(x_source_);
- XCloseDisplay(g_xdisplay);
- g_xdisplay = NULL;
-}
-
-// static
-Display* MessagePumpX11::GetDefaultXDisplay() {
- if (!g_xdisplay)
- g_xdisplay = XOpenDisplay(NULL);
- return g_xdisplay;
-}
-
-#if defined(TOOLKIT_GTK)
-// static
-MessagePumpX11* MessagePumpX11::Current() {
- MessageLoop* loop = MessageLoop::current();
- return static_cast<MessagePumpX11*>(loop->pump_gpu());
-}
-#else
-// static
-MessagePumpX11* MessagePumpX11::Current() {
- MessageLoopForUI* loop = MessageLoopForUI::current();
- return static_cast<MessagePumpX11*>(loop->pump_ui());
-}
-#endif
-
-void MessagePumpX11::AddDispatcherForWindow(
- MessagePumpDispatcher* dispatcher,
- unsigned long xid) {
- dispatchers_.insert(std::make_pair(xid, dispatcher));
-}
-
-void MessagePumpX11::RemoveDispatcherForWindow(unsigned long xid) {
- dispatchers_.erase(xid);
-}
-
-void MessagePumpX11::AddDispatcherForRootWindow(
- MessagePumpDispatcher* dispatcher) {
- root_window_dispatchers_.AddObserver(dispatcher);
-}
-
-void MessagePumpX11::RemoveDispatcherForRootWindow(
- MessagePumpDispatcher* dispatcher) {
- root_window_dispatchers_.RemoveObserver(dispatcher);
-}
-
-void MessagePumpX11::AddObserver(MessagePumpObserver* observer) {
- observers_.AddObserver(observer);
-}
-
-void MessagePumpX11::RemoveObserver(MessagePumpObserver* observer) {
- observers_.RemoveObserver(observer);
-}
-
-bool MessagePumpX11::DispatchXEvents() {
- Display* display = GetDefaultXDisplay();
- DCHECK(display);
- MessagePumpDispatcher* dispatcher =
- GetDispatcher() ? GetDispatcher() : this;
-
- // In the general case, we want to handle all pending events before running
- // the tasks. This is what happens in the message_pump_glib case.
- while (XPending(display)) {
- XEvent xev;
- XNextEvent(display, &xev);
- if (dispatcher && ProcessXEvent(dispatcher, &xev))
- return TRUE;
- }
- return TRUE;
-}
-
-void MessagePumpX11::BlockUntilWindowMapped(unsigned long xid) {
- XEvent event;
-
- Display* display = GetDefaultXDisplay();
- DCHECK(display);
-
- MessagePumpDispatcher* dispatcher =
- GetDispatcher() ? GetDispatcher() : this;
-
- do {
- // Block until there's a message of |event_mask| type on |w|. Then remove
- // it from the queue and stuff it in |event|.
- XWindowEvent(display, xid, StructureNotifyMask, &event);
- ProcessXEvent(dispatcher, &event);
- } while (event.type != MapNotify);
-}
-
-void MessagePumpX11::InitXSource() {
- // CHECKs are to help track down crbug.com/113106.
- CHECK(!x_source_);
- Display* display = GetDefaultXDisplay();
- CHECK(display) << "Unable to get connection to X server";
- x_poll_.reset(new GPollFD());
- CHECK(x_poll_.get());
- x_poll_->fd = ConnectionNumber(display);
- x_poll_->events = G_IO_IN;
-
- x_source_ = g_source_new(&XSourceFuncs, sizeof(GSource));
- g_source_add_poll(x_source_, x_poll_.get());
- g_source_set_can_recurse(x_source_, TRUE);
- g_source_set_callback(x_source_, NULL, this, NULL);
- g_source_attach(x_source_, g_main_context_default());
-}
-
-bool MessagePumpX11::ProcessXEvent(MessagePumpDispatcher* dispatcher,
- XEvent* xev) {
- bool should_quit = false;
-
- bool have_cookie = false;
- if (xev->type == GenericEvent &&
- XGetEventData(xev->xgeneric.display, &xev->xcookie)) {
- have_cookie = true;
- }
-
- if (!WillProcessXEvent(xev)) {
- if (!dispatcher->Dispatch(xev)) {
- should_quit = true;
- Quit();
- }
- DidProcessXEvent(xev);
- }
-
- if (have_cookie) {
- XFreeEventData(xev->xgeneric.display, &xev->xcookie);
- }
-
- return should_quit;
-}
-
-bool MessagePumpX11::WillProcessXEvent(XEvent* xevent) {
- if (!observers().might_have_observers())
- return false;
- ObserverListBase<MessagePumpObserver>::Iterator it(observers());
- MessagePumpObserver* obs;
- while ((obs = it.GetNext()) != NULL) {
- if (obs->WillProcessEvent(xevent))
- return true;
- }
- return false;
-}
-
-void MessagePumpX11::DidProcessXEvent(XEvent* xevent) {
- FOR_EACH_OBSERVER(MessagePumpObserver, observers(), DidProcessEvent(xevent));
-}
-
-MessagePumpDispatcher* MessagePumpX11::GetDispatcherForXEvent(
- const NativeEvent& xev) const {
- ::Window x_window = FindEventTarget(xev);
- DispatchersMap::const_iterator it = dispatchers_.find(x_window);
- return it != dispatchers_.end() ? it->second : NULL;
-}
-
-bool MessagePumpX11::Dispatch(const NativeEvent& xev) {
- // MappingNotify events (meaning that the keyboard or pointer buttons have
- // been remapped) aren't associated with a window; send them to all
- // dispatchers.
- if (xev->type == MappingNotify) {
- for (DispatchersMap::const_iterator it = dispatchers_.begin();
- it != dispatchers_.end(); ++it) {
- it->second->Dispatch(xev);
- }
- return true;
- }
-
- if (FindEventTarget(xev) == x_root_window_) {
- FOR_EACH_OBSERVER(MessagePumpDispatcher, root_window_dispatchers_,
- Dispatch(xev));
- return true;
- }
- MessagePumpDispatcher* dispatcher = GetDispatcherForXEvent(xev);
- return dispatcher ? dispatcher->Dispatch(xev) : true;
-}
-
-} // namespace base
diff --git a/chromium/base/message_loop/message_pump_x11.h b/chromium/base/message_loop/message_pump_x11.h
deleted file mode 100644
index 015c230a700..00000000000
--- a/chromium/base/message_loop/message_pump_x11.h
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H
-#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H
-
-#include <bitset>
-#include <map>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_pump.h"
-#include "base/message_loop/message_pump_dispatcher.h"
-#include "base/message_loop/message_pump_glib.h"
-#include "base/message_loop/message_pump_observer.h"
-#include "base/observer_list.h"
-
-// It would be nice to include the X11 headers here so that we use Window
-// instead of its typedef of unsigned long, but we can't because everything in
-// chrome includes us through base/message_loop/message_loop.h, and X11's crappy
-// #define heavy headers muck up half of chrome.
-
-typedef struct _GPollFD GPollFD;
-typedef struct _GSource GSource;
-typedef struct _XDisplay Display;
-
-namespace base {
-
-// This class implements a message-pump for dispatching X events.
-//
-// If there's a current dispatcher given through RunWithDispatcher(), that
-// dispatcher receives events. Otherwise, we route to messages to dispatchers
-// who have subscribed to messages from a specific X11 window.
-class BASE_EXPORT MessagePumpX11 : public MessagePumpGlib,
- public MessagePumpDispatcher {
- public:
- MessagePumpX11();
- virtual ~MessagePumpX11();
-
- // Returns default X Display.
- static Display* GetDefaultXDisplay();
-
- // Returns the UI or GPU message pump.
- static MessagePumpX11* Current();
-
- // Adds/Removes |dispatcher| for the |xid|. This will route all messages from
- // the window |xid| to |dispatcher.
- void AddDispatcherForWindow(MessagePumpDispatcher* dispatcher,
- unsigned long xid);
- void RemoveDispatcherForWindow(unsigned long xid);
-
- // Adds/Removes |dispatcher| to receive all events sent to the X root
- // window. A root window can have multiple dispatchers, and events on root
- // windows will be dispatched to all.
- void AddDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
- void RemoveDispatcherForRootWindow(MessagePumpDispatcher* dispatcher);
-
- // Adds an Observer, which will start receiving notifications immediately.
- void AddObserver(MessagePumpObserver* observer);
-
- // Removes an Observer. It is safe to call this method while an Observer is
- // receiving a notification callback.
- void RemoveObserver(MessagePumpObserver* observer);
-
- // Internal function. Called by the glib source dispatch function. Processes
- // all available X events.
- bool DispatchXEvents();
-
- // Blocks on the X11 event queue until we receive notification from the
- // xserver that |w| has been mapped; StructureNotifyMask events on |w| are
- // pulled out from the queue and dispatched out of order.
- //
- // For those that know X11, this is really a wrapper around XWindowEvent
- // which still makes sure the preempted event is dispatched instead of
- // dropped on the floor. This method exists because mapping a window is
- // asynchronous (and we receive an XEvent when mapped), while there are also
- // functions which require a mapped window.
- void BlockUntilWindowMapped(unsigned long xid);
-
- private:
- typedef std::map<unsigned long, MessagePumpDispatcher*> DispatchersMap;
-
- // Initializes the glib event source for X.
- void InitXSource();
-
- // Dispatches the XEvent and returns true if we should exit the current loop
- // of message processing.
- bool ProcessXEvent(MessagePumpDispatcher* dispatcher, XEvent* event);
-
- // Sends the event to the observers. If an observer returns true, then it does
- // not send the event to any other observers and returns true. Returns false
- // if no observer returns true.
- bool WillProcessXEvent(XEvent* xevent);
- void DidProcessXEvent(XEvent* xevent);
-
- // Returns the Dispatcher based on the event's target window.
- MessagePumpDispatcher* GetDispatcherForXEvent(const NativeEvent& xev) const;
-
- ObserverList<MessagePumpObserver>& observers() { return observers_; }
-
- // Overridden from MessagePumpDispatcher:
- virtual bool Dispatch(const NativeEvent& event) OVERRIDE;
-
- // The event source for X events.
- GSource* x_source_;
-
- // The poll attached to |x_source_|.
- scoped_ptr<GPollFD> x_poll_;
-
- DispatchersMap dispatchers_;
-
- // Dispatch calls can cause addition of new dispatchers as we iterate
- // through them. Use ObserverList to ensure the iterator remains valid across
- // additions.
- ObserverList<MessagePumpDispatcher> root_window_dispatchers_;
-
- // List of observers.
- ObserverList<MessagePumpObserver> observers_;
-
- unsigned long x_root_window_;
-
- DISALLOW_COPY_AND_ASSIGN(MessagePumpX11);
-};
-
-#if !defined(TOOLKIT_GTK)
-typedef MessagePumpX11 MessagePumpForUI;
-#endif
-
-} // namespace base
-
-#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_X11_H
diff --git a/chromium/base/message_loop/timer_slack.h b/chromium/base/message_loop/timer_slack.h
new file mode 100644
index 00000000000..1ad6ca94a1b
--- /dev/null
+++ b/chromium/base/message_loop/timer_slack.h
@@ -0,0 +1,22 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_MESSAGE_LOOP_TIMER_SLACK_H_
+#define BASE_MESSAGE_LOOP_TIMER_SLACK_H_
+
+namespace base {
+
+// Amount of timer slack to use for delayed timers. Increasing timer slack
+// allows the OS to coalesce timers more effectively.
+enum TimerSlack {
+ // Lowest value for timer slack allowed by OS.
+ TIMER_SLACK_NONE,
+
+ // Maximal value for timer slack allowed by OS.
+ TIMER_SLACK_MAXIMUM
+};
+
+} // namespace base
+
+#endif // BASE_MESSAGE_LOOP_TIMER_SLACK_H_
diff --git a/chromium/base/metrics/OWNERS b/chromium/base/metrics/OWNERS
index 17a19451a29..3fd7c0dbc2b 100644
--- a/chromium/base/metrics/OWNERS
+++ b/chromium/base/metrics/OWNERS
@@ -1,8 +1,3 @@
-# Primary OWNER
-jar@chromium.org
-
-# Secondary OWNER; can review simpler changes
+asvitkine@chromium.org
isherman@chromium.org
-
-# Note that all members of the parent file base/OWNERS can also stamp trivial
-# changes, but will probably defer to Jim for meatier changes.
+jar@chromium.org
diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc
index b99f3148f96..54b95211470 100644
--- a/chromium/base/metrics/field_trial.cc
+++ b/chromium/base/metrics/field_trial.cc
@@ -252,7 +252,7 @@ FieldTrialList::FieldTrialList(
FieldTrialList::~FieldTrialList() {
AutoLock auto_lock(lock_);
while (!registered_.empty()) {
- RegistrationList::iterator it = registered_.begin();
+ RegistrationMap::iterator it = registered_.begin();
it->second->Release();
registered_.erase(it->first);
}
@@ -390,7 +390,7 @@ void FieldTrialList::GetActiveFieldTrialGroups(
return;
AutoLock auto_lock(global_->lock_);
- for (RegistrationList::iterator it = global_->registered_.begin();
+ for (RegistrationMap::iterator it = global_->registered_.begin();
it != global_->registered_.end(); ++it) {
FieldTrial::ActiveGroup active_group;
if (it->second->GetActiveGroup(&active_group))
@@ -399,8 +399,10 @@ void FieldTrialList::GetActiveFieldTrialGroups(
}
// static
-bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string,
- FieldTrialActivationMode mode) {
+bool FieldTrialList::CreateTrialsFromString(
+ const std::string& trials_string,
+ FieldTrialActivationMode mode,
+ const std::set<std::string>& ignored_trial_names) {
DCHECK(global_);
if (trials_string.empty() || !global_)
return true;
@@ -419,6 +421,9 @@ bool FieldTrialList::CreateTrialsFromString(const std::string& trials_string,
group_name_end - name_end - 1);
next_item = group_name_end + 1;
+ if (ignored_trial_names.find(name) != ignored_trial_names.end())
+ continue;
+
FieldTrial* trial = CreateFieldTrial(name, group_name);
if (!trial)
return false;
@@ -513,7 +518,7 @@ const FieldTrial::EntropyProvider*
}
FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) {
- RegistrationList::iterator it = registered_.find(name);
+ RegistrationMap::iterator it = registered_.find(name);
if (registered_.end() == it)
return NULL;
return it->second;
diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h
index 70ce2f9c45d..51177eb93f7 100644
--- a/chromium/base/metrics/field_trial.h
+++ b/chromium/base/metrics/field_trial.h
@@ -55,6 +55,7 @@
#define BASE_METRICS_FIELD_TRIAL_H_
#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -412,9 +413,12 @@ class BASE_EXPORT FieldTrialList {
// browser process into this non-browser process, but could also be invoked
// through a command line argument to the browser process. The created field
// trials are marked as "used" for the purposes of active trial reporting if
- // |mode| is ACTIVATE_TRIALS.
- static bool CreateTrialsFromString(const std::string& prior_trials,
- FieldTrialActivationMode mode);
+ // |mode| is ACTIVATE_TRIALS. Trial names in |ignored_trial_names| are ignored
+ // when parsing |prior_trials|.
+ static bool CreateTrialsFromString(
+ const std::string& prior_trials,
+ FieldTrialActivationMode mode,
+ const std::set<std::string>& ignored_trial_names);
// Create a FieldTrial with the given |name| and using 100% probability for
// the FieldTrial, force FieldTrial to have the same group string as
@@ -441,7 +445,7 @@ class BASE_EXPORT FieldTrialList {
private:
// A map from FieldTrial names to the actual instances.
- typedef std::map<std::string, FieldTrial*> RegistrationList;
+ typedef std::map<std::string, FieldTrial*> RegistrationMap;
// If one-time randomization is enabled, returns a weak pointer to the
// corresponding EntropyProvider. Otherwise, returns NULL.
@@ -466,7 +470,7 @@ class BASE_EXPORT FieldTrialList {
// Lock for access to registered_.
base::Lock lock_;
- RegistrationList registered_;
+ RegistrationMap registered_;
// Entropy provider to be used for one-time randomized field trials. If NULL,
// one-time randomization is not supported.
diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc
index a77633e8f09..866095ca66e 100644
--- a/chromium/base/metrics/field_trial_unittest.cc
+++ b/chromium/base/metrics/field_trial_unittest.cc
@@ -385,7 +385,8 @@ TEST_F(FieldTrialTest, Restore) {
ASSERT_FALSE(FieldTrialList::TrialExists("xxx"));
FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/",
- FieldTrialList::DONT_ACTIVATE_TRIALS);
+ FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>());
FieldTrial* trial = FieldTrialList::Find("Some_name");
ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
@@ -400,13 +401,17 @@ TEST_F(FieldTrialTest, Restore) {
TEST_F(FieldTrialTest, BogusRestore) {
EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
- "MissingSlash", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "MissingSlash", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
- "MissingGroupName/", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "MissingGroupName/", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
- "MissingFinalSlash/gname", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "MissingFinalSlash/gname", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
- "noname, only group/", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "noname, only group/", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
}
TEST_F(FieldTrialTest, DuplicateRestore) {
@@ -420,18 +425,21 @@ TEST_F(FieldTrialTest, DuplicateRestore) {
// It is OK if we redundantly specify a winner.
EXPECT_TRUE(FieldTrialList::CreateTrialsFromString(
- save_string, FieldTrialList::DONT_ACTIVATE_TRIALS));
+ save_string, FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
// But it is an error to try to change to a different winner.
EXPECT_FALSE(FieldTrialList::CreateTrialsFromString(
- "Some name/Loser/", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "Some name/Loser/", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
}
TEST_F(FieldTrialTest, CreateTrialsFromStringActive) {
ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
ASSERT_FALSE(FieldTrialList::TrialExists("Xyz"));
ASSERT_TRUE(FieldTrialList::CreateTrialsFromString(
- "Abc/def/Xyz/zyx/", FieldTrialList::ACTIVATE_TRIALS));
+ "Abc/def/Xyz/zyx/", FieldTrialList::ACTIVATE_TRIALS,
+ std::set<std::string>()));
FieldTrial::ActiveGroups active_groups;
FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
@@ -446,7 +454,8 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActive) {
ASSERT_FALSE(FieldTrialList::TrialExists("Abc"));
ASSERT_FALSE(FieldTrialList::TrialExists("Xyz"));
ASSERT_TRUE(FieldTrialList::CreateTrialsFromString(
- "Abc/def/Xyz/zyx/", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "Abc/def/Xyz/zyx/", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
FieldTrial::ActiveGroups active_groups;
FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
@@ -469,7 +478,7 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringActiveObserver) {
TestFieldTrialObserver observer;
ASSERT_TRUE(FieldTrialList::CreateTrialsFromString(
- "Abc/def/", FieldTrialList::ACTIVATE_TRIALS));
+ "Abc/def/", FieldTrialList::ACTIVATE_TRIALS, std::set<std::string>()));
RunLoop().RunUntilIdle();
EXPECT_EQ("Abc", observer.trial_name());
@@ -481,7 +490,8 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) {
TestFieldTrialObserver observer;
ASSERT_TRUE(FieldTrialList::CreateTrialsFromString(
- "Abc/def/", FieldTrialList::DONT_ACTIVATE_TRIALS));
+ "Abc/def/", FieldTrialList::DONT_ACTIVATE_TRIALS,
+ std::set<std::string>()));
RunLoop().RunUntilIdle();
// Observer shouldn't be notified.
EXPECT_TRUE(observer.trial_name().empty());
@@ -494,6 +504,48 @@ TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) {
EXPECT_EQ("def", observer.group_name());
}
+TEST_F(FieldTrialTest, CreateTrialsFromStringWithIgnoredFieldTrials) {
+ ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted1"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("Foo"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted2"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("Bar"));
+ ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted3"));
+
+ std::set<std::string> ignored_trial_names;
+ ignored_trial_names.insert("Unaccepted1");
+ ignored_trial_names.insert("Unaccepted2");
+ ignored_trial_names.insert("Unaccepted3");
+
+ FieldTrialList::CreateTrialsFromString(
+ "Unaccepted1/Unaccepted1_name/"
+ "Foo/Foo_name/"
+ "Unaccepted2/Unaccepted2_name/"
+ "Bar/Bar_name/"
+ "Unaccepted3/Unaccepted3_name/",
+ FieldTrialList::DONT_ACTIVATE_TRIALS,
+ ignored_trial_names);
+
+ EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted1"));
+ EXPECT_TRUE(FieldTrialList::TrialExists("Foo"));
+ EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted2"));
+ EXPECT_TRUE(FieldTrialList::TrialExists("Bar"));
+ EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted3"));
+
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_TRUE(active_groups.empty());
+
+ FieldTrial* trial = FieldTrialList::Find("Foo");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("Foo", trial->trial_name());
+ EXPECT_EQ("Foo_name", trial->group_name());
+
+ trial = FieldTrialList::Find("Bar");
+ ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
+ EXPECT_EQ("Bar", trial->trial_name());
+ EXPECT_EQ("Bar_name", trial->group_name());
+}
+
TEST_F(FieldTrialTest, CreateFieldTrial) {
ASSERT_FALSE(FieldTrialList::TrialExists("Some_name"));
diff --git a/chromium/base/metrics/histogram.cc b/chromium/base/metrics/histogram.cc
index fbe66d05d29..beb9b9e8898 100644
--- a/chromium/base/metrics/histogram.cc
+++ b/chromium/base/metrics/histogram.cc
@@ -110,7 +110,16 @@ HistogramBase* Histogram::FactoryGet(const string& name,
}
DCHECK_EQ(HISTOGRAM, histogram->GetHistogramType());
- CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
+ if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
+ // The construction arguments do not match the existing histogram. This can
+ // come about if an extension updates in the middle of a chrome run and has
+ // changed one of them, or simply by bad code within Chrome itself. We
+ // return NULL here with the expectation that bad code in Chrome will crash
+ // on dereference, but extension/Pepper APIs will guard against NULL and not
+ // crash.
+ DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
+ return NULL;
+ }
return histogram;
}
@@ -567,7 +576,16 @@ HistogramBase* LinearHistogram::FactoryGetWithRangeDescription(
}
DCHECK_EQ(LINEAR_HISTOGRAM, histogram->GetHistogramType());
- CHECK(histogram->HasConstructionArguments(minimum, maximum, bucket_count));
+ if (!histogram->HasConstructionArguments(minimum, maximum, bucket_count)) {
+ // The construction arguments do not match the existing histogram. This can
+ // come about if an extension updates in the middle of a chrome run and has
+ // changed one of them, or simply by bad code within Chrome itself. We
+ // return NULL here with the expectation that bad code in Chrome will crash
+ // on dereference, but extension/Pepper APIs will guard against NULL and not
+ // crash.
+ DLOG(ERROR) << "Histogram " << name << " has bad construction arguments";
+ return NULL;
+ }
return histogram;
}
diff --git a/chromium/base/metrics/histogram.h b/chromium/base/metrics/histogram.h
index 9845362d98a..b795a12dbd1 100644
--- a/chromium/base/metrics/histogram.h
+++ b/chromium/base/metrics/histogram.h
@@ -155,8 +155,8 @@ class Lock;
base::subtle::Release_Store(&atomic_histogram_pointer, \
reinterpret_cast<base::subtle::AtomicWord>(histogram_pointer)); \
} \
- DCHECK_EQ(histogram_pointer->histogram_name(), \
- std::string(constant_histogram_name)); \
+ if (DCHECK_IS_ON) \
+ histogram_pointer->CheckName(constant_histogram_name); \
histogram_pointer->histogram_add_method_invocation; \
} while (0)
@@ -190,6 +190,12 @@ class Lock;
base::Histogram::FactoryGet(name, min, max, bucket_count, \
base::HistogramBase::kNoFlags))
+// This is a helper macro used by other macros and shouldn't be used directly.
+#define HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary, flag) \
+ STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
+ base::LinearHistogram::FactoryGet(name, 1, boundary, boundary + 1, \
+ flag))
+
#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) \
HISTOGRAM_ENUMERATION(name, under_one_hundred, 101)
@@ -349,9 +355,15 @@ class Lock;
// The samples should always be strictly less than |boundary_value|. For more
// details, see the comment for the |HISTOGRAM_ENUMERATION| macro, above.
#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
- STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
- base::LinearHistogram::FactoryGet(name, 1, boundary_value, \
- boundary_value + 1, base::HistogramBase::kUmaTargetedHistogramFlag))
+ HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \
+ base::HistogramBase::kUmaTargetedHistogramFlag)
+
+// Similar to UMA_HISTOGRAM_ENUMERATION, but used for recording stability
+// histograms. Use this if recording a histogram that should be part of the
+// initial stability log.
+#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \
+ HISTOGRAM_ENUMERATION_WITH_FLAG(name, sample, boundary_value, \
+ base::HistogramBase::kUmaStabilityHistogramFlag)
#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \
STATIC_HISTOGRAM_POINTER_BLOCK(name, Add(sample), \
diff --git a/chromium/base/metrics/histogram_base.cc b/chromium/base/metrics/histogram_base.cc
index 5c6e2d271bb..6e7e69e061c 100644
--- a/chromium/base/metrics/histogram_base.cc
+++ b/chromium/base/metrics/histogram_base.cc
@@ -20,7 +20,7 @@
namespace base {
std::string HistogramTypeToString(HistogramType type) {
- switch(type) {
+ switch (type) {
case HISTOGRAM:
return "HISTOGRAM";
case LINEAR_HISTOGRAM:
@@ -66,6 +66,10 @@ HistogramBase::HistogramBase(const std::string& name)
HistogramBase::~HistogramBase() {}
+void HistogramBase::CheckName(const StringPiece& name) const {
+ DCHECK_EQ(histogram_name(), name);
+}
+
void HistogramBase::SetFlags(int32 flags) {
flags_ |= flags;
}
diff --git a/chromium/base/metrics/histogram_base.h b/chromium/base/metrics/histogram_base.h
index 4248bd84a4c..be07fc6d419 100644
--- a/chromium/base/metrics/histogram_base.h
+++ b/chromium/base/metrics/histogram_base.h
@@ -12,6 +12,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_piece.h"
#include "base/time/time.h"
class Pickle;
@@ -48,20 +49,28 @@ BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo(
class BASE_EXPORT HistogramBase {
public:
- typedef int Sample; // Used for samples.
- typedef subtle::Atomic32 Count; // Used to count samples.
+ typedef int Sample; // Used for samples.
+ typedef subtle::Atomic32 AtomicCount; // Used to count samples.
+ typedef int32 Count; // Used to manipulate counts in temporaries.
static const Sample kSampleType_MAX; // INT_MAX
enum Flags {
kNoFlags = 0,
- kUmaTargetedHistogramFlag = 0x1, // Histogram should be UMA uploaded.
- // Indicate that the histogram was pickled to be sent across an IPC Channel.
- // If we observe this flag on a histogram being aggregated into after IPC,
- // then we are running in a single process mode, and the aggregation should
- // not take place (as we would be aggregating back into the source
- // histogram!).
+ // Histogram should be UMA uploaded.
+ kUmaTargetedHistogramFlag = 0x1,
+
+ // Indicates that this is a stability histogram. This flag exists to specify
+ // which histograms should be included in the initial stability log. Please
+ // refer to |MetricsService::PrepareInitialStabilityLog|.
+ kUmaStabilityHistogramFlag = kUmaTargetedHistogramFlag | 0x2,
+
+ // Indicates that the histogram was pickled to be sent across an IPC
+ // Channel. If we observe this flag on a histogram being aggregated into
+ // after IPC, then we are running in a single process mode, and the
+ // aggregation should not take place (as we would be aggregating back into
+ // the source histogram!).
kIPCSerializationSourceFlag = 0x10,
// Only for Histogram and its sub classes: fancy bucket-naming support.
@@ -84,6 +93,11 @@ class BASE_EXPORT HistogramBase {
std::string histogram_name() const { return histogram_name_; }
+ // Comapres |name| to the histogram name and triggers a DCHECK if they do not
+ // match. This is a helper function used by histogram macros, which results in
+ // in more compact machine code being generated by the macros.
+ void CheckName(const StringPiece& name) const;
+
// Operations with Flags enum.
int32 flags() const { return flags_; }
void SetFlags(int32 flags);
@@ -129,7 +143,7 @@ class BASE_EXPORT HistogramBase {
// customize the output.
void WriteJSON(std::string* output) const;
-protected:
+ protected:
// Subclasses should implement this function to make SerializeInfo work.
virtual bool SerializeInfoImpl(Pickle* pickle) const = 0;
diff --git a/chromium/base/metrics/histogram_delta_serialization.cc b/chromium/base/metrics/histogram_delta_serialization.cc
index 924916db76e..e4aad13ac22 100644
--- a/chromium/base/metrics/histogram_delta_serialization.cc
+++ b/chromium/base/metrics/histogram_delta_serialization.cc
@@ -7,8 +7,8 @@
#include "base/logging.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_snapshot_manager.h"
+#include "base/numerics/safe_conversions.h"
#include "base/pickle.h"
-#include "base/safe_numerics.h"
#include "base/values.h"
namespace base {
@@ -66,7 +66,7 @@ void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
// the histograms, so that the receiving process can distinguish them from the
// local histograms.
histogram_snapshot_manager_.PrepareDeltas(
- Histogram::kIPCSerializationSourceFlag, false);
+ Histogram::kIPCSerializationSourceFlag, Histogram::kNoFlags);
serialized_deltas_ = NULL;
}
@@ -75,7 +75,7 @@ void HistogramDeltaSerialization::DeserializeAndAddSamples(
const std::vector<std::string>& serialized_deltas) {
for (std::vector<std::string>::const_iterator it = serialized_deltas.begin();
it != serialized_deltas.end(); ++it) {
- Pickle pickle(it->data(), checked_numeric_cast<int>(it->size()));
+ Pickle pickle(it->data(), checked_cast<int>(it->size()));
PickleIterator iter(pickle);
DeserializeHistogramAndAddSamples(&iter);
}
diff --git a/chromium/base/metrics/histogram_samples.cc b/chromium/base/metrics/histogram_samples.cc
index 9f3dd6a40e0..2319ed1572f 100644
--- a/chromium/base/metrics/histogram_samples.cc
+++ b/chromium/base/metrics/histogram_samples.cc
@@ -64,7 +64,10 @@ HistogramSamples::~HistogramSamples() {}
void HistogramSamples::Add(const HistogramSamples& other) {
sum_ += other.sum();
- redundant_count_ += other.redundant_count();
+ HistogramBase::Count old_redundant_count =
+ subtle::NoBarrier_Load(&redundant_count_);
+ subtle::NoBarrier_Store(&redundant_count_,
+ old_redundant_count + other.redundant_count());
bool success = AddSubtractImpl(other.Iterator().get(), ADD);
DCHECK(success);
}
@@ -76,7 +79,10 @@ bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
if (!iter->ReadInt64(&sum) || !iter->ReadInt(&redundant_count))
return false;
sum_ += sum;
- redundant_count_ += redundant_count;
+ HistogramBase::Count old_redundant_count =
+ subtle::NoBarrier_Load(&redundant_count_);
+ subtle::NoBarrier_Store(&redundant_count_,
+ old_redundant_count + redundant_count);
SampleCountPickleIterator pickle_iter(iter);
return AddSubtractImpl(&pickle_iter, ADD);
@@ -84,13 +90,17 @@ bool HistogramSamples::AddFromPickle(PickleIterator* iter) {
void HistogramSamples::Subtract(const HistogramSamples& other) {
sum_ -= other.sum();
- redundant_count_ -= other.redundant_count();
+ HistogramBase::Count old_redundant_count =
+ subtle::NoBarrier_Load(&redundant_count_);
+ subtle::NoBarrier_Store(&redundant_count_,
+ old_redundant_count - other.redundant_count());
bool success = AddSubtractImpl(other.Iterator().get(), SUBTRACT);
DCHECK(success);
}
bool HistogramSamples::Serialize(Pickle* pickle) const {
- if (!pickle->WriteInt64(sum_) || !pickle->WriteInt(redundant_count_))
+ if (!pickle->WriteInt64(sum_) ||
+ !pickle->WriteInt(subtle::NoBarrier_Load(&redundant_count_)))
return false;
HistogramBase::Sample min;
@@ -113,8 +123,8 @@ void HistogramSamples::IncreaseSum(int64 diff) {
}
void HistogramSamples::IncreaseRedundantCount(HistogramBase::Count diff) {
- base::subtle::NoBarrier_Store(&redundant_count_,
- base::subtle::NoBarrier_Load(&redundant_count_) + diff);
+ subtle::NoBarrier_Store(&redundant_count_,
+ subtle::NoBarrier_Load(&redundant_count_) + diff);
}
SampleCountIterator::~SampleCountIterator() {}
diff --git a/chromium/base/metrics/histogram_samples.h b/chromium/base/metrics/histogram_samples.h
index c55b58442c5..c4c0a9842b7 100644
--- a/chromium/base/metrics/histogram_samples.h
+++ b/chromium/base/metrics/histogram_samples.h
@@ -39,7 +39,9 @@ class BASE_EXPORT HistogramSamples {
// Accessor fuctions.
int64 sum() const { return sum_; }
- HistogramBase::Count redundant_count() const { return redundant_count_; }
+ HistogramBase::Count redundant_count() const {
+ return subtle::NoBarrier_Load(&redundant_count_);
+ }
protected:
// Based on |op| type, add or subtract sample counts data from the iterator.
@@ -59,7 +61,7 @@ class BASE_EXPORT HistogramSamples {
// types, there might be races during histogram accumulation and snapshotting
// that we choose to accept. In this case, the tallies might mismatch even
// when no memory corruption has happened.
- HistogramBase::Count redundant_count_;
+ HistogramBase::AtomicCount redundant_count_;
};
class BASE_EXPORT SampleCountIterator {
diff --git a/chromium/base/metrics/histogram_snapshot_manager.cc b/chromium/base/metrics/histogram_snapshot_manager.cc
index cb594bd96d9..b1e26da59dc 100644
--- a/chromium/base/metrics/histogram_snapshot_manager.cc
+++ b/chromium/base/metrics/histogram_snapshot_manager.cc
@@ -25,18 +25,17 @@ HistogramSnapshotManager::~HistogramSnapshotManager() {
STLDeleteValues(&logged_samples_);
}
-void HistogramSnapshotManager::PrepareDeltas(HistogramBase::Flags flag_to_set,
- bool record_only_uma) {
+void HistogramSnapshotManager::PrepareDeltas(
+ HistogramBase::Flags flag_to_set,
+ HistogramBase::Flags required_flags) {
StatisticsRecorder::Histograms histograms;
StatisticsRecorder::GetHistograms(&histograms);
for (StatisticsRecorder::Histograms::const_iterator it = histograms.begin();
histograms.end() != it;
++it) {
(*it)->SetFlags(flag_to_set);
- if (record_only_uma &&
- 0 == ((*it)->flags() & Histogram::kUmaTargetedHistogramFlag))
- continue;
- PrepareDelta(**it);
+ if (((*it)->flags() & required_flags) == required_flags)
+ PrepareDelta(**it);
}
}
diff --git a/chromium/base/metrics/histogram_snapshot_manager.h b/chromium/base/metrics/histogram_snapshot_manager.h
index 3c7050859e3..5a5f2e93e5e 100644
--- a/chromium/base/metrics/histogram_snapshot_manager.h
+++ b/chromium/base/metrics/histogram_snapshot_manager.h
@@ -29,9 +29,13 @@ class BASE_EXPORT HistogramSnapshotManager {
virtual ~HistogramSnapshotManager();
// Snapshot all histograms, and ask |histogram_flattener_| to record the
- // delta. The arguments allow selecting only a subset of histograms for
- // recording, or to set a flag in each recorded histogram.
- void PrepareDeltas(HistogramBase::Flags flags_to_set, bool record_only_uma);
+ // delta. |flags_to_set| is used to set flags for each histogram.
+ // |required_flags| is used to select histograms to be recorded.
+ // Only histograms that have all the flags specified by the argument will be
+ // chosen. If all histograms should be recorded, set it to
+ // |Histogram::kNoFlags|.
+ void PrepareDeltas(HistogramBase::Flags flags_to_set,
+ HistogramBase::Flags required_flags);
private:
// Snapshot this histogram, and record the delta.
diff --git a/chromium/base/metrics/histogram_snapshot_manager_unittest.cc b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc
new file mode 100644
index 00000000000..e6672ea7eb2
--- /dev/null
+++ b/chromium/base/metrics/histogram_snapshot_manager_unittest.cc
@@ -0,0 +1,106 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/histogram_snapshot_manager.h"
+
+#include <string>
+#include <vector>
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_delta_serialization.h"
+#include "base/metrics/statistics_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+class HistogramFlattenerDeltaRecorder : public HistogramFlattener {
+ public:
+ HistogramFlattenerDeltaRecorder() {}
+
+ virtual void RecordDelta(const HistogramBase& histogram,
+ const HistogramSamples& snapshot) OVERRIDE {
+ recorded_delta_histogram_names_.push_back(histogram.histogram_name());
+ }
+
+ virtual void InconsistencyDetected(
+ HistogramBase::Inconsistency problem) OVERRIDE {
+ ASSERT_TRUE(false);
+ }
+
+ virtual void UniqueInconsistencyDetected(
+ HistogramBase::Inconsistency problem) OVERRIDE {
+ ASSERT_TRUE(false);
+ }
+
+ virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE {
+ ASSERT_TRUE(false);
+ }
+
+ std::vector<std::string> GetRecordedDeltaHistogramNames() {
+ return recorded_delta_histogram_names_;
+ }
+
+ private:
+ std::vector<std::string> recorded_delta_histogram_names_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistogramFlattenerDeltaRecorder);
+};
+
+class HistogramSnapshotManagerTest : public testing::Test {
+ protected:
+ HistogramSnapshotManagerTest()
+ : histogram_snapshot_manager_(&histogram_flattener_delta_recorder_) {}
+
+ virtual ~HistogramSnapshotManagerTest() {}
+
+ StatisticsRecorder statistics_recorder_;
+ HistogramFlattenerDeltaRecorder histogram_flattener_delta_recorder_;
+ HistogramSnapshotManager histogram_snapshot_manager_;
+};
+
+TEST_F(HistogramSnapshotManagerTest, PrepareDeltasNoFlagsFilter) {
+ // kNoFlags filter should record all histograms.
+ UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2);
+ UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
+
+ histogram_snapshot_manager_.PrepareDeltas(HistogramBase::kNoFlags,
+ HistogramBase::kNoFlags);
+
+ const std::vector<std::string>& histograms =
+ histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
+ EXPECT_EQ(2U, histograms.size());
+ EXPECT_EQ("UmaHistogram", histograms[0]);
+ EXPECT_EQ("UmaStabilityHistogram", histograms[1]);
+}
+
+TEST_F(HistogramSnapshotManagerTest, PrepareDeltasUmaHistogramFlagFilter) {
+ // Note that kUmaStabilityHistogramFlag includes kUmaTargetedHistogramFlag.
+ UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2);
+ UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
+
+ histogram_snapshot_manager_.PrepareDeltas(
+ HistogramBase::kNoFlags, HistogramBase::kUmaTargetedHistogramFlag);
+
+ const std::vector<std::string>& histograms =
+ histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
+ EXPECT_EQ(2U, histograms.size());
+ EXPECT_EQ("UmaHistogram", histograms[0]);
+ EXPECT_EQ("UmaStabilityHistogram", histograms[1]);
+}
+
+TEST_F(HistogramSnapshotManagerTest,
+ PrepareDeltasUmaStabilityHistogramFlagFilter) {
+ UMA_HISTOGRAM_ENUMERATION("UmaHistogram", 1, 2);
+ UMA_STABILITY_HISTOGRAM_ENUMERATION("UmaStabilityHistogram", 1, 2);
+
+ histogram_snapshot_manager_.PrepareDeltas(
+ HistogramBase::kNoFlags, HistogramBase::kUmaStabilityHistogramFlag);
+
+ const std::vector<std::string>& histograms =
+ histogram_flattener_delta_recorder_.GetRecordedDeltaHistogramNames();
+ EXPECT_EQ(1U, histograms.size());
+ EXPECT_EQ("UmaStabilityHistogram", histograms[0]);
+}
+
+} // namespace base
diff --git a/chromium/base/metrics/histogram_unittest.cc b/chromium/base/metrics/histogram_unittest.cc
index e7a01aa5af3..5fbd28a1069 100644
--- a/chromium/base/metrics/histogram_unittest.cc
+++ b/chromium/base/metrics/histogram_unittest.cc
@@ -443,6 +443,32 @@ TEST_F(HistogramTest, CustomHistogramSerializeInfo) {
EXPECT_FALSE(iter.SkipBytes(1));
}
+TEST_F(HistogramTest, BadConstruction) {
+ HistogramBase* histogram = Histogram::FactoryGet(
+ "BadConstruction", 0, 100, 8, HistogramBase::kNoFlags);
+ EXPECT_TRUE(histogram->HasConstructionArguments(1, 100, 8));
+
+ // Try to get the same histogram name with different arguments.
+ HistogramBase* bad_histogram = Histogram::FactoryGet(
+ "BadConstruction", 0, 100, 7, HistogramBase::kNoFlags);
+ EXPECT_EQ(NULL, bad_histogram);
+ bad_histogram = Histogram::FactoryGet(
+ "BadConstruction", 0, 99, 8, HistogramBase::kNoFlags);
+ EXPECT_EQ(NULL, bad_histogram);
+
+ HistogramBase* linear_histogram = LinearHistogram::FactoryGet(
+ "BadConstructionLinear", 0, 100, 8, HistogramBase::kNoFlags);
+ EXPECT_TRUE(linear_histogram->HasConstructionArguments(1, 100, 8));
+
+ // Try to get the same histogram name with different arguments.
+ bad_histogram = LinearHistogram::FactoryGet(
+ "BadConstructionLinear", 0, 100, 7, HistogramBase::kNoFlags);
+ EXPECT_EQ(NULL, bad_histogram);
+ bad_histogram = LinearHistogram::FactoryGet(
+ "BadConstructionLinear", 10, 100, 8, HistogramBase::kNoFlags);
+ EXPECT_EQ(NULL, bad_histogram);
+}
+
#if GTEST_HAS_DEATH_TEST
// For Histogram, LinearHistogram and CustomHistogram, the minimum for a
// declared range is 1, while the maximum is (HistogramBase::kSampleType_MAX -
diff --git a/chromium/base/metrics/sample_vector.cc b/chromium/base/metrics/sample_vector.cc
index 89233c74839..ca3205e9759 100644
--- a/chromium/base/metrics/sample_vector.cc
+++ b/chromium/base/metrics/sample_vector.cc
@@ -32,20 +32,20 @@ void SampleVector::Accumulate(Sample value, Count count) {
Count SampleVector::GetCount(Sample value) const {
size_t bucket_index = GetBucketIndex(value);
- return counts_[bucket_index];
+ return subtle::NoBarrier_Load(&counts_[bucket_index]);
}
Count SampleVector::TotalCount() const {
Count count = 0;
for (size_t i = 0; i < counts_.size(); i++) {
- count += counts_[i];
+ count += subtle::NoBarrier_Load(&counts_[i]);
}
return count;
}
Count SampleVector::GetCountAtIndex(size_t bucket_index) const {
DCHECK(bucket_index < counts_.size());
- return counts_[bucket_index];
+ return subtle::NoBarrier_Load(&counts_[bucket_index]);
}
scoped_ptr<SampleCountIterator> SampleVector::Iterator() const {
@@ -66,7 +66,10 @@ bool SampleVector::AddSubtractImpl(SampleCountIterator* iter,
if (min == bucket_ranges_->range(index) &&
max == bucket_ranges_->range(index + 1)) {
// Sample matches this bucket!
- counts_[index] += (op == HistogramSamples::ADD) ? count : -count;
+ HistogramBase::Count old_counts =
+ subtle::NoBarrier_Load(&counts_[index]);
+ subtle::NoBarrier_Store(&counts_[index],
+ old_counts + ((op == HistogramSamples::ADD) ? count : -count));
iter->Next();
} else if (min > bucket_ranges_->range(index)) {
// Sample is larger than current bucket range. Try next.
@@ -138,7 +141,7 @@ void SampleVectorIterator::Get(HistogramBase::Sample* min,
if (max != NULL)
*max = bucket_ranges_->range(index_ + 1);
if (count != NULL)
- *count = (*counts_)[index_];
+ *count = subtle::NoBarrier_Load(&(*counts_)[index_]);
}
bool SampleVectorIterator::GetBucketIndex(size_t* index) const {
@@ -153,7 +156,7 @@ void SampleVectorIterator::SkipEmptyBuckets() {
return;
while (index_ < counts_->size()) {
- if ((*counts_)[index_] != 0)
+ if (subtle::NoBarrier_Load(&(*counts_)[index_]) != 0)
return;
index_++;
}
diff --git a/chromium/base/metrics/sample_vector.h b/chromium/base/metrics/sample_vector.h
index 67c344a97c3..6b2adcf2094 100644
--- a/chromium/base/metrics/sample_vector.h
+++ b/chromium/base/metrics/sample_vector.h
@@ -46,7 +46,7 @@ class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples {
private:
FRIEND_TEST_ALL_PREFIXES(HistogramTest, CorruptSampleCounts);
- std::vector<HistogramBase::Count> counts_;
+ std::vector<HistogramBase::AtomicCount> counts_;
// Shares the same BucketRanges with Histogram object.
const BucketRanges* const bucket_ranges_;
@@ -56,7 +56,7 @@ class BASE_EXPORT_PRIVATE SampleVector : public HistogramSamples {
class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator {
public:
- SampleVectorIterator(const std::vector<HistogramBase::Count>* counts,
+ SampleVectorIterator(const std::vector<HistogramBase::AtomicCount>* counts,
const BucketRanges* bucket_ranges);
virtual ~SampleVectorIterator();
@@ -73,7 +73,7 @@ class BASE_EXPORT_PRIVATE SampleVectorIterator : public SampleCountIterator {
private:
void SkipEmptyBuckets();
- const std::vector<HistogramBase::Count>* counts_;
+ const std::vector<HistogramBase::AtomicCount>* counts_;
const BucketRanges* bucket_ranges_;
size_t index_;
diff --git a/chromium/base/metrics/statistics_recorder.h b/chromium/base/metrics/statistics_recorder.h
index 0716e80572d..3bef622b76f 100644
--- a/chromium/base/metrics/statistics_recorder.h
+++ b/chromium/base/metrics/statistics_recorder.h
@@ -30,7 +30,7 @@ class BASE_EXPORT StatisticsRecorder {
public:
typedef std::vector<HistogramBase*> Histograms;
- // Initializes the StatisticsRecorder system.
+ // Initializes the StatisticsRecorder system. Safe to call multiple times.
static void Initialize();
// Find out if histograms can now be registered into our list.
@@ -70,9 +70,9 @@ class BASE_EXPORT StatisticsRecorder {
static HistogramBase* FindHistogram(const std::string& name);
// GetSnapshot copies some of the pointers to registered histograms into the
- // caller supplied vector (Histograms). Only histograms with names matching
- // query are returned. The query must be a substring of histogram name for its
- // pointer to be copied.
+ // caller supplied vector (Histograms). Only histograms which have |query| as
+ // a substring are copied (an empty string will process all registered
+ // histograms).
static void GetSnapshot(const std::string& query, Histograms* snapshot);
private:
@@ -86,6 +86,7 @@ class BASE_EXPORT StatisticsRecorder {
friend struct DefaultLazyInstanceTraits<StatisticsRecorder>;
friend class HistogramBaseTest;
+ friend class HistogramSnapshotManagerTest;
friend class HistogramTest;
friend class SparseHistogramTest;
friend class StatisticsRecorderTest;
diff --git a/chromium/base/metrics/stats_table.cc b/chromium/base/metrics/stats_table.cc
index 716b7cfc91d..03158fdbd0a 100644
--- a/chromium/base/metrics/stats_table.cc
+++ b/chromium/base/metrics/stats_table.cc
@@ -14,12 +14,6 @@
#include "base/threading/platform_thread.h"
#include "base/threading/thread_local_storage.h"
-#if defined(OS_POSIX)
-#include "base/posix/global_descriptors.h"
-#include "errno.h"
-#include "ipc/ipc_descriptors.h"
-#endif
-
namespace base {
// The StatsTable uses a shared memory segment that is laid out as follows
@@ -105,7 +99,7 @@ class StatsTable::Internal {
// Construct a new Internal based on expected size parameters, or
// return NULL on failure.
- static Internal* New(const std::string& name,
+ static Internal* New(const StatsTable::TableIdentifier& table,
int size,
int max_threads,
int max_counters);
@@ -151,8 +145,9 @@ class StatsTable::Internal {
}
// Create or open the SharedMemory used by the stats table.
- static SharedMemory* CreateSharedMemory(const std::string& name,
- int size);
+ static SharedMemory* CreateSharedMemory(
+ const StatsTable::TableIdentifier& table,
+ int size);
// Initializes the table on first access. Sets header values
// appropriately and zeroes all counters.
@@ -174,11 +169,12 @@ class StatsTable::Internal {
};
// static
-StatsTable::Internal* StatsTable::Internal::New(const std::string& name,
- int size,
- int max_threads,
- int max_counters) {
- scoped_ptr<SharedMemory> shared_memory(CreateSharedMemory(name, size));
+StatsTable::Internal* StatsTable::Internal::New(
+ const StatsTable::TableIdentifier& table,
+ int size,
+ int max_threads,
+ int max_counters) {
+ scoped_ptr<SharedMemory> shared_memory(CreateSharedMemory(table, size));
if (!shared_memory.get())
return NULL;
if (!shared_memory->Map(size))
@@ -200,16 +196,14 @@ StatsTable::Internal* StatsTable::Internal::New(const std::string& name,
}
// static
-SharedMemory* StatsTable::Internal::CreateSharedMemory(const std::string& name,
- int size) {
+SharedMemory* StatsTable::Internal::CreateSharedMemory(
+ const StatsTable::TableIdentifier& table,
+ int size) {
#if defined(OS_POSIX)
- GlobalDescriptors* global_descriptors = GlobalDescriptors::GetInstance();
- if (global_descriptors->MaybeGet(kStatsTableSharedMemFd) != -1) {
- // Open the shared memory file descriptor passed by the browser process.
- FileDescriptor file_descriptor(
- global_descriptors->Get(kStatsTableSharedMemFd), false);
- return new SharedMemory(file_descriptor, false);
- }
+ // Check for existing table.
+ if (table.fd != -1)
+ return new SharedMemory(table, false);
+
// Otherwise we need to create it.
scoped_ptr<SharedMemory> shared_memory(new SharedMemory());
if (!shared_memory->CreateAnonymous(size))
@@ -217,15 +211,22 @@ SharedMemory* StatsTable::Internal::CreateSharedMemory(const std::string& name,
return shared_memory.release();
#elif defined(OS_WIN)
scoped_ptr<SharedMemory> shared_memory(new SharedMemory());
- if (!shared_memory->CreateNamed(name, true, size))
- return NULL;
+ if (table.empty()) {
+ // Create an anonymous table.
+ if (!shared_memory->CreateAnonymous(size))
+ return NULL;
+ } else {
+ // Create a named table for sharing between processes.
+ if (!shared_memory->CreateNamedDeprecated(table, true, size))
+ return NULL;
+ }
return shared_memory.release();
#endif
}
void StatsTable::Internal::InitializeTable(void* memory, int size,
- int max_counters,
- int max_threads) {
+ int max_counters,
+ int max_threads) {
// Zero everything.
memset(memory, 0, size);
@@ -286,7 +287,8 @@ struct StatsTable::TLSData {
// We keep a singleton table which can be easily accessed.
StatsTable* global_table = NULL;
-StatsTable::StatsTable(const std::string& name, int max_threads,
+StatsTable::StatsTable(const TableIdentifier& table,
+ int max_threads,
int max_counters)
: internal_(NULL),
tls_index_(SlotReturnFunction) {
@@ -298,7 +300,7 @@ StatsTable::StatsTable(const std::string& name, int max_threads,
AlignedSize(max_threads * sizeof(int)) +
AlignedSize((sizeof(int) * (max_counters * max_threads)));
- internal_ = Internal::New(name, table_size, max_threads, max_counters);
+ internal_ = Internal::New(table, table_size, max_threads, max_counters);
if (!internal_)
DPLOG(ERROR) << "StatsTable did not initialize";
@@ -344,8 +346,9 @@ int StatsTable::RegisterThread(const std::string& name) {
// Registering a thread requires that we lock the shared memory
// so that two threads don't grab the same slot. Fortunately,
// thread creation shouldn't happen in inner loops.
+ // TODO(viettrungluu): crbug.com/345734: Use a different locking mechanism.
{
- SharedMemoryAutoLock lock(internal_->shared_memory());
+ SharedMemoryAutoLockDeprecated lock(internal_->shared_memory());
slot = FindEmptyThread();
if (!slot) {
return 0;
@@ -568,7 +571,7 @@ int StatsTable::AddCounter(const std::string& name) {
{
// To add a counter to the shared memory, we need the
// shared memory lock.
- SharedMemoryAutoLock lock(internal_->shared_memory());
+ SharedMemoryAutoLockDeprecated lock(internal_->shared_memory());
// We have space, so create a new counter.
counter_id = FindCounterOrEmptyRow(name);
diff --git a/chromium/base/metrics/stats_table.h b/chromium/base/metrics/stats_table.h
index 49ba79f88ba..719e6304813 100644
--- a/chromium/base/metrics/stats_table.h
+++ b/chromium/base/metrics/stats_table.h
@@ -28,24 +28,45 @@
#include "base/memory/shared_memory.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_local_storage.h"
+#include "build/build_config.h"
+
+#if defined(OS_POSIX)
+#include "base/file_descriptor_posix.h"
+#endif
namespace base {
class BASE_EXPORT StatsTable {
public:
- // Create a new StatsTable.
- // If a StatsTable already exists with the specified name, this StatsTable
- // will use the same shared memory segment as the original. Otherwise,
- // a new StatsTable is created and all counters are zeroed.
+ // Identifies a StatsTable. We often want to share these between processes.
+ //
+ // On Windows, we use a named shared memory segment so the table identifier
+ // should be a relatively unique string identifying the table to use. An
+ // empty string can be used to use an anonymous shared memory segment for
+ // cases where the table does not need to be shared between processes.
//
- // name is the name of the StatsTable to use.
+ // Posix does not support named memory so we explicitly share file
+ // descriptors. On Posix, pass a default-constructed file descriptor if a
+ // handle doesn't already exist, and a new one will be created.
+ //
+ // If a table doesn't already exist with the given identifier, a new one will
+ // be created with zeroed counters.
+#if defined(OS_POSIX)
+ typedef FileDescriptor TableIdentifier;
+#elif defined(OS_WIN)
+ typedef std::string TableIdentifier;
+#endif
+
+ // Create a new StatsTable.
//
// max_threads is the maximum number of threads the table will support.
// If the StatsTable already exists, this number is ignored.
//
// max_counters is the maximum number of counters the table will support.
// If the StatsTable already exists, this number is ignored.
- StatsTable(const std::string& name, int max_threads, int max_counters);
+ StatsTable(const TableIdentifier& table,
+ int max_threads,
+ int max_counters);
// Destroys the StatsTable. When the last StatsTable is destroyed
// (across all processes), the StatsTable is removed from disk.
diff --git a/chromium/base/metrics/stats_table_unittest.cc b/chromium/base/metrics/stats_table_unittest.cc
index 8fd33971f4f..53a47315e6f 100644
--- a/chromium/base/metrics/stats_table_unittest.cc
+++ b/chromium/base/metrics/stats_table_unittest.cc
@@ -18,21 +18,14 @@
namespace base {
class StatsTableTest : public MultiProcessTest {
- public:
- void DeleteShmem(const std::string& name) {
- SharedMemory mem;
- mem.Delete(name);
- }
};
// Open a StatsTable and verify that we can write to each of the
// locations in the table.
TEST_F(StatsTableTest, VerifySlots) {
- const std::string kTableName = "VerifySlotsStatTable";
const int kMaxThreads = 1;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
// Register a single thread.
std::string thread_name = "mainThread";
@@ -55,8 +48,6 @@ TEST_F(StatsTableTest, VerifySlots) {
// Try to allocate an additional counter. Verify it fails.
int counter_id = table.FindCounter(counter_base_name);
EXPECT_EQ(counter_id, 0);
-
- DeleteShmem(kTableName);
}
// CounterZero will continually be set to 0.
@@ -117,11 +108,9 @@ void StatsTableThread::Run() {
#endif
TEST_F(StatsTableTest, MAYBE_MultipleThreads) {
// Create a stats table.
- const std::string kTableName = "MultipleThreadStatTable";
const int kMaxThreads = 20;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
StatsTable::set_current(&table);
EXPECT_EQ(0, table.CountThreadsRegistered());
@@ -166,10 +155,11 @@ TEST_F(StatsTableTest, MAYBE_MultipleThreads) {
EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
table.GetCounterValue(name));
EXPECT_EQ(0, table.CountThreadsRegistered());
-
- DeleteShmem(kTableName);
}
+// This multiprocess test only runs on Windows. On Posix, the shared memory
+// handle is not sent between the processes properly.
+#if defined(OS_WIN)
const std::string kMPTableName = "MultipleProcessStatTable";
MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) {
@@ -199,7 +189,6 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) {
// Create a stats table.
const int kMaxProcs = 20;
const int kMaxCounter = 5;
- DeleteShmem(kMPTableName);
StatsTable table(kMPTableName, kMaxProcs, kMaxCounter);
StatsTable::set_current(&table);
EXPECT_EQ(0, table.CountThreadsRegistered());
@@ -211,7 +200,7 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) {
// Spawn the processes.
for (int16 index = 0; index < kMaxProcs; index++) {
- procs[index] = this->SpawnChild("StatsTableMultipleProcessMain", false);
+ procs[index] = SpawnChild("StatsTableMultipleProcessMain");
EXPECT_NE(kNullProcessHandle, procs[index]);
}
@@ -241,9 +230,8 @@ TEST_F(StatsTableTest, DISABLED_MultipleProcesses) {
EXPECT_EQ(-kMaxProcs * kThreadLoops,
table.GetCounterValue(name));
EXPECT_EQ(0, table.CountThreadsRegistered());
-
- DeleteShmem(kMPTableName);
}
+#endif
class MockStatsCounter : public StatsCounter {
public:
@@ -255,11 +243,9 @@ class MockStatsCounter : public StatsCounter {
// Test some basic StatsCounter operations
TEST_F(StatsTableTest, StatsCounter) {
// Create a stats table.
- const std::string kTableName = "StatTable";
const int kMaxThreads = 20;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
StatsTable::set_current(&table);
MockStatsCounter foo("foo");
@@ -295,8 +281,6 @@ TEST_F(StatsTableTest, StatsCounter) {
EXPECT_EQ(-1, table.GetCounterValue("c:foo"));
foo.Subtract(-1);
EXPECT_EQ(0, table.GetCounterValue("c:foo"));
-
- DeleteShmem(kTableName);
}
class MockStatsCounterTimer : public StatsCounterTimer {
@@ -311,11 +295,9 @@ class MockStatsCounterTimer : public StatsCounterTimer {
// Test some basic StatsCounterTimer operations
TEST_F(StatsTableTest, StatsCounterTimer) {
// Create a stats table.
- const std::string kTableName = "StatTable";
const int kMaxThreads = 20;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
StatsTable::set_current(&table);
MockStatsCounterTimer bar("bar");
@@ -340,17 +322,14 @@ TEST_F(StatsTableTest, StatsCounterTimer) {
bar.Stop();
EXPECT_GT(table.GetCounterValue("t:bar"), 0);
EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar"));
- DeleteShmem(kTableName);
}
// Test some basic StatsRate operations
TEST_F(StatsTableTest, StatsRate) {
// Create a stats table.
- const std::string kTableName = "StatTable";
const int kMaxThreads = 20;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
StatsTable::set_current(&table);
StatsRate baz("baz");
@@ -375,17 +354,14 @@ TEST_F(StatsTableTest, StatsRate) {
baz.Stop();
EXPECT_EQ(2, table.GetCounterValue("c:baz"));
EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:baz"));
- DeleteShmem(kTableName);
}
// Test some basic StatsScope operations
TEST_F(StatsTableTest, StatsScope) {
// Create a stats table.
- const std::string kTableName = "StatTable";
const int kMaxThreads = 20;
const int kMaxCounter = 5;
- DeleteShmem(kTableName);
- StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable table(StatsTable::TableIdentifier(), kMaxThreads, kMaxCounter);
StatsTable::set_current(&table);
StatsCounterTimer foo("foo");
@@ -417,8 +393,6 @@ TEST_F(StatsTableTest, StatsScope) {
EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:foo"));
EXPECT_LE(kDuration.InMilliseconds() * 2, table.GetCounterValue("t:bar"));
EXPECT_EQ(2, table.GetCounterValue("c:bar"));
-
- DeleteShmem(kTableName);
}
} // namespace base
diff --git a/chromium/base/metrics/user_metrics.cc b/chromium/base/metrics/user_metrics.cc
new file mode 100644
index 00000000000..9db5840ab57
--- /dev/null
+++ b/chromium/base/metrics/user_metrics.cc
@@ -0,0 +1,74 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/metrics/user_metrics.h"
+
+#include <vector>
+
+#include "base/lazy_instance.h"
+#include "base/threading/thread_checker.h"
+
+namespace base {
+namespace {
+
+// A helper class for tracking callbacks and ensuring thread-safety.
+class Callbacks {
+ public:
+ Callbacks() {}
+
+ // Records the |action|.
+ void Record(const std::string& action) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (size_t i = 0; i < callbacks_.size(); ++i) {
+ callbacks_[i].Run(action);
+ }
+ }
+
+ // Adds |callback| to the list of |callbacks_|.
+ void AddCallback(const ActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ callbacks_.push_back(callback);
+ }
+
+ // Removes the first instance of |callback| from the list of |callbacks_|, if
+ // there is one.
+ void RemoveCallback(const ActionCallback& callback) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ for (size_t i = 0; i < callbacks_.size(); ++i) {
+ if (callbacks_[i].Equals(callback)) {
+ callbacks_.erase(callbacks_.begin() + i);
+ return;
+ }
+ }
+ }
+
+ private:
+ base::ThreadChecker thread_checker_;
+ std::vector<ActionCallback> callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(Callbacks);
+};
+
+base::LazyInstance<Callbacks> g_callbacks = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+void RecordAction(const UserMetricsAction& action) {
+ g_callbacks.Get().Record(action.str_);
+}
+
+void RecordComputedAction(const std::string& action) {
+ g_callbacks.Get().Record(action);
+}
+
+void AddActionCallback(const ActionCallback& callback) {
+ g_callbacks.Get().AddCallback(callback);
+}
+
+void RemoveActionCallback(const ActionCallback& callback) {
+ g_callbacks.Get().RemoveCallback(callback);
+
+}
+
+} // namespace base
diff --git a/chromium/base/metrics/user_metrics.h b/chromium/base/metrics/user_metrics.h
new file mode 100644
index 00000000000..bcfefb80994
--- /dev/null
+++ b/chromium/base/metrics/user_metrics.h
@@ -0,0 +1,60 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_METRICS_USER_METRICS_H_
+#define BASE_METRICS_USER_METRICS_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/metrics/user_metrics_action.h"
+
+namespace base {
+
+// This module provides some helper functions for logging actions tracked by
+// the user metrics system.
+
+// Record that the user performed an action.
+// This method *must* be called from the main thread.
+//
+// "Action" here means a user-generated event:
+// good: "Reload", "CloseTab", and "IMEInvoked"
+// not good: "SSLDialogShown", "PageLoaded", "DiskFull"
+// We use this to gather anonymized information about how users are
+// interacting with the browser.
+// WARNING: In calls to this function, UserMetricsAction and a
+// string literal parameter must be on the same line, e.g.
+// RecordAction(UserMetricsAction("my extremely long action name"));
+// This ensures that our processing scripts can associate this action's hash
+// with its metric name. Therefore, it will be possible to retrieve the metric
+// name from the hash later on.
+//
+// Once a new recorded action is added, run
+// tools/metrics/actions/extract_actions.py
+// to add the metric to actions.xml, then update the <owner>s and <description>
+// sections. Make sure to include the actions.xml file when you upload your code
+// for review!
+//
+// For more complicated situations (like when there are many different
+// possible actions), see RecordComputedAction.
+BASE_EXPORT void RecordAction(const UserMetricsAction& action);
+
+// This function has identical input and behavior to RecordAction, but is
+// not automatically found by the action-processing scripts. It can be used
+// when it's a pain to enumerate all possible actions, but if you use this
+// you need to also update the rules for extracting known actions in
+// tools/metrics/actions/extract_actions.py.
+BASE_EXPORT void RecordComputedAction(const std::string& action);
+
+// Called with the action string.
+typedef base::Callback<void(const std::string&)> ActionCallback;
+
+// Add/remove action callbacks (see above).
+BASE_EXPORT void AddActionCallback(const ActionCallback& callback);
+BASE_EXPORT void RemoveActionCallback(const ActionCallback& callback);
+
+} // namespace base
+
+#endif // BASE_METRICS_USER_METRICS_H_
diff --git a/chromium/base/metrics/user_metrics_action.h b/chromium/base/metrics/user_metrics_action.h
new file mode 100644
index 00000000000..8c195b3e803
--- /dev/null
+++ b/chromium/base/metrics/user_metrics_action.h
@@ -0,0 +1,28 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_METRICS_USER_METRICS_ACTION_H_
+#define BASE_METRICS_USER_METRICS_ACTION_H_
+
+namespace base {
+
+// UserMetricsAction exists purely to standardize on the parameters passed to
+// UserMetrics. That way, our toolset can scan the source code reliable for
+// constructors and extract the associated string constants.
+// WARNING: When using UserMetricsAction, UserMetricsAction and a string literal
+// parameter must be on the same line, e.g.
+// RecordAction(UserMetricsAction("my extremely long action name"));
+// or
+// RenderThread::Get()->RecordAction(
+// UserMetricsAction("my extremely long action name"));
+// because otherwise our processing scripts won't pick up on new actions.
+// Please see tools/metrics/actions/extract_actions.py for details.
+struct UserMetricsAction {
+ const char* str_;
+ explicit UserMetricsAction(const char* str) : str_(str) {}
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_USER_METRICS_ACTION_H_
diff --git a/chromium/base/native_library.h b/chromium/base/native_library.h
index 891f35bd540..1e764da89aa 100644
--- a/chromium/base/native_library.h
+++ b/chromium/base/native_library.h
@@ -8,7 +8,11 @@
// This file defines a cross-platform "NativeLibrary" type which represents
// a loadable module.
+#include <string>
+
#include "base/base_export.h"
+#include "base/compiler_specific.h"
+#include "base/strings/string16.h"
#include "build/build_config.h"
#if defined(OS_WIN)
@@ -17,15 +21,6 @@
#import <CoreFoundation/CoreFoundation.h>
#endif // OS_*
-#include "base/strings/string16.h"
-
-// Macro useful for writing cross-platform function pointers.
-#if defined(OS_WIN) && !defined(CDECL)
-#define CDECL __cdecl
-#else
-#define CDECL
-#endif
-
namespace base {
class FilePath;
@@ -56,12 +51,26 @@ typedef NativeLibraryStruct* NativeLibrary;
typedef void* NativeLibrary;
#endif // OS_*
+struct BASE_EXPORT NativeLibraryLoadError {
+#if defined(OS_WIN)
+ NativeLibraryLoadError() : code(0) {}
+#endif // OS_WIN
+
+ // Returns a string representation of the load error.
+ std::string ToString() const;
+
+#if defined(OS_WIN)
+ DWORD code;
+#else
+ std::string message;
+#endif // OS_WIN
+};
+
// Loads a native library from disk. Release it with UnloadNativeLibrary when
// you're done. Returns NULL on failure.
-// If |err| is not NULL, it may be filled in with an error message on
-// error.
+// If |error| is not NULL, it may be filled in on load error.
BASE_EXPORT NativeLibrary LoadNativeLibrary(const FilePath& library_path,
- std::string* error);
+ NativeLibraryLoadError* error);
#if defined(OS_WIN)
// Loads a native library from disk. Release it with UnloadNativeLibrary when
diff --git a/chromium/base/native_library_mac.mm b/chromium/base/native_library_mac.mm
index 6544fcaed99..6696454c6ef 100644
--- a/chromium/base/native_library_mac.mm
+++ b/chromium/base/native_library_mac.mm
@@ -41,9 +41,14 @@ static NativeLibraryObjCStatus GetObjCStatusForImage(
return section == NULL ? OBJC_NOT_PRESENT : OBJC_PRESENT;
}
+std::string NativeLibraryLoadError::ToString() const {
+ return message;
+}
+
// static
+// TODO(xhwang): Fill |error|. See http://crbug.com/353771
NativeLibrary LoadNativeLibrary(const base::FilePath& library_path,
- std::string* error) {
+ NativeLibraryLoadError* /* error */) {
// dlopen() etc. open the file off disk.
if (library_path.Extension() == "dylib" || !DirectoryExists(library_path)) {
void* dylib = dlopen(library_path.value().c_str(), RTLD_LAZY);
diff --git a/chromium/base/native_library_posix.cc b/chromium/base/native_library_posix.cc
index dfa20fc01cf..3179a93833c 100644
--- a/chromium/base/native_library_posix.cc
+++ b/chromium/base/native_library_posix.cc
@@ -13,9 +13,13 @@
namespace base {
+std::string NativeLibraryLoadError::ToString() const {
+ return message;
+}
+
// static
NativeLibrary LoadNativeLibrary(const FilePath& library_path,
- std::string* error) {
+ NativeLibraryLoadError* error) {
// dlopen() opens the file off disk.
base::ThreadRestrictions::AssertIOAllowed();
@@ -25,7 +29,7 @@ NativeLibrary LoadNativeLibrary(const FilePath& library_path,
// and http://crbug.com/40794.
void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY);
if (!dl && error)
- *error = dlerror();
+ error->message = dlerror();
return dl;
}
diff --git a/chromium/base/native_library_win.cc b/chromium/base/native_library_win.cc
index 2d437fa2a58..43528b977e4 100644
--- a/chromium/base/native_library_win.cc
+++ b/chromium/base/native_library_win.cc
@@ -7,6 +7,7 @@
#include <windows.h>
#include "base/file_util.h"
+#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
@@ -14,34 +15,48 @@ namespace base {
typedef HMODULE (WINAPI* LoadLibraryFunction)(const wchar_t* file_name);
+namespace {
+
NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
- LoadLibraryFunction load_library_api) {
+ LoadLibraryFunction load_library_api,
+ NativeLibraryLoadError* error) {
// LoadLibrary() opens the file off disk.
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
// Switch the current directory to the library directory as the library
// may have dependencies on DLLs in this directory.
bool restore_directory = false;
FilePath current_directory;
- if (file_util::GetCurrentDirectory(&current_directory)) {
+ if (GetCurrentDirectory(&current_directory)) {
FilePath plugin_path = library_path.DirName();
if (!plugin_path.empty()) {
- file_util::SetCurrentDirectory(plugin_path);
+ SetCurrentDirectory(plugin_path);
restore_directory = true;
}
}
HMODULE module = (*load_library_api)(library_path.value().c_str());
+ if (!module && error) {
+ // GetLastError() needs to be called immediately after |load_library_api|.
+ error->code = GetLastError();
+ }
+
if (restore_directory)
- file_util::SetCurrentDirectory(current_directory);
+ SetCurrentDirectory(current_directory);
return module;
}
+} // namespace
+
+std::string NativeLibraryLoadError::ToString() const {
+ return StringPrintf("%u", code);
+}
+
// static
NativeLibrary LoadNativeLibrary(const FilePath& library_path,
- std::string* error) {
- return LoadNativeLibraryHelper(library_path, LoadLibraryW);
+ NativeLibraryLoadError* error) {
+ return LoadNativeLibraryHelper(library_path, LoadLibraryW, error);
}
NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) {
@@ -51,7 +66,7 @@ NativeLibrary LoadNativeLibraryDynamically(const FilePath& library_path) {
load_library = reinterpret_cast<LoadLibraryFunction>(
GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW"));
- return LoadNativeLibraryHelper(library_path, load_library);
+ return LoadNativeLibraryHelper(library_path, load_library, NULL);
}
// static
diff --git a/chromium/base/nix/mime_util_xdg.cc b/chromium/base/nix/mime_util_xdg.cc
index dd2c7eb663c..f78b6aba558 100644
--- a/chromium/base/nix/mime_util_xdg.cc
+++ b/chromium/base/nix/mime_util_xdg.cc
@@ -4,572 +4,21 @@
#include "base/nix/mime_util_xdg.h"
-#include <cstdlib>
-#include <list>
-#include <map>
-#include <vector>
-
-#include "base/environment.h"
-#include "base/file_util.h"
+#include "base/files/file_path.h"
#include "base/lazy_instance.h"
-#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/singleton.h"
-#include "base/nix/xdg_util.h"
-#include "base/strings/string_split.h"
-#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "base/third_party/xdg_mime/xdgmime.h"
#include "base/threading/thread_restrictions.h"
-#include "base/time/time.h"
namespace base {
namespace nix {
namespace {
-class IconTheme;
-
// None of the XDG stuff is thread-safe, so serialize all access under
// this lock.
LazyInstance<Lock>::Leaky g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER;
-class MimeUtilConstants {
- public:
- typedef std::map<std::string, IconTheme*> IconThemeMap;
- typedef std::map<FilePath, Time> IconDirMtimeMap;
- typedef std::vector<std::string> IconFormats;
-
- // Specified by XDG icon theme specs.
- static const int kUpdateIntervalInSeconds = 5;
-
- static const size_t kDefaultThemeNum = 4;
-
- static MimeUtilConstants* GetInstance() {
- return Singleton<MimeUtilConstants>::get();
- }
-
- // Store icon directories and their mtimes.
- IconDirMtimeMap icon_dirs_;
-
- // Store icon formats.
- IconFormats icon_formats_;
-
- // Store loaded icon_theme.
- IconThemeMap icon_themes_;
-
- // The default theme.
- IconTheme* default_themes_[kDefaultThemeNum];
-
- TimeTicks last_check_time_;
-
- // The current icon theme, usually set through GTK theme integration.
- std::string icon_theme_name_;
-
- private:
- MimeUtilConstants() {
- icon_formats_.push_back(".png");
- icon_formats_.push_back(".svg");
- icon_formats_.push_back(".xpm");
-
- for (size_t i = 0; i < kDefaultThemeNum; ++i)
- default_themes_[i] = NULL;
- }
- ~MimeUtilConstants();
-
- friend struct DefaultSingletonTraits<MimeUtilConstants>;
-
- DISALLOW_COPY_AND_ASSIGN(MimeUtilConstants);
-};
-
-// IconTheme represents an icon theme as defined by the xdg icon theme spec.
-// Example themes on GNOME include 'Human' and 'Mist'.
-// Example themes on KDE include 'crystalsvg' and 'kdeclassic'.
-class IconTheme {
- public:
- // A theme consists of multiple sub-directories, like '32x32' and 'scalable'.
- class SubDirInfo {
- public:
- // See spec for details.
- enum Type {
- Fixed,
- Scalable,
- Threshold
- };
- SubDirInfo()
- : size(0),
- type(Threshold),
- max_size(0),
- min_size(0),
- threshold(2) {
- }
- size_t size; // Nominal size of the icons in this directory.
- Type type; // Type of the icon size.
- size_t max_size; // Maximum size that the icons can be scaled to.
- size_t min_size; // Minimum size that the icons can be scaled to.
- size_t threshold; // Maximum difference from desired size. 2 by default.
- };
-
- explicit IconTheme(const std::string& name);
-
- ~IconTheme() {}
-
- // Returns the path to an icon with the name |icon_name| and a size of |size|
- // pixels. If the icon does not exist, but |inherits| is true, then look for
- // the icon in the parent theme.
- FilePath GetIconPath(const std::string& icon_name, int size, bool inherits);
-
- // Load a theme with the name |theme_name| into memory. Returns null if theme
- // is invalid.
- static IconTheme* LoadTheme(const std::string& theme_name);
-
- private:
- // Returns the path to an icon with the name |icon_name| in |subdir|.
- FilePath GetIconPathUnderSubdir(const std::string& icon_name,
- const std::string& subdir);
-
- // Whether the theme loaded properly.
- bool IsValid() {
- return index_theme_loaded_;
- }
-
- // Read and parse |file| which is usually named 'index.theme' per theme spec.
- bool LoadIndexTheme(const FilePath& file);
-
- // Checks to see if the icons in |info| matches |size| (in pixels). Returns
- // 0 if they match, or the size difference in pixels.
- size_t MatchesSize(SubDirInfo* info, size_t size);
-
- // Yet another function to read a line.
- std::string ReadLine(FILE* fp);
-
- // Set directories to search for icons to the comma-separated list |dirs|.
- bool SetDirectories(const std::string& dirs);
-
- bool index_theme_loaded_; // True if an instance is properly loaded.
- // store the scattered directories of this theme.
- std::list<FilePath> dirs_;
-
- // store the subdirs of this theme and array index of |info_array_|.
- std::map<std::string, int> subdirs_;
- scoped_ptr<SubDirInfo[]> info_array_; // List of sub-directories.
- std::string inherits_; // Name of the theme this one inherits from.
-};
-
-IconTheme::IconTheme(const std::string& name)
- : index_theme_loaded_(false) {
- ThreadRestrictions::AssertIOAllowed();
- // Iterate on all icon directories to find directories of the specified
- // theme and load the first encountered index.theme.
- MimeUtilConstants::IconDirMtimeMap::iterator iter;
- FilePath theme_path;
- MimeUtilConstants::IconDirMtimeMap* icon_dirs =
- &MimeUtilConstants::GetInstance()->icon_dirs_;
- for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
- theme_path = iter->first.Append(name);
- if (!DirectoryExists(theme_path))
- continue;
- FilePath theme_index = theme_path.Append("index.theme");
- if (!index_theme_loaded_ && PathExists(theme_index)) {
- if (!LoadIndexTheme(theme_index))
- return;
- index_theme_loaded_ = true;
- }
- dirs_.push_back(theme_path);
- }
-}
-
-FilePath IconTheme::GetIconPath(const std::string& icon_name, int size,
- bool inherits) {
- std::map<std::string, int>::iterator subdir_iter;
- FilePath icon_path;
-
- for (subdir_iter = subdirs_.begin();
- subdir_iter != subdirs_.end();
- ++subdir_iter) {
- SubDirInfo* info = &info_array_[subdir_iter->second];
- if (MatchesSize(info, size) == 0) {
- icon_path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
- if (!icon_path.empty())
- return icon_path;
- }
- }
- // Now looking for the mostly matched.
- size_t min_delta_seen = 9999;
-
- for (subdir_iter = subdirs_.begin();
- subdir_iter != subdirs_.end();
- ++subdir_iter) {
- SubDirInfo* info = &info_array_[subdir_iter->second];
- size_t delta = MatchesSize(info, size);
- if (delta < min_delta_seen) {
- FilePath path = GetIconPathUnderSubdir(icon_name, subdir_iter->first);
- if (!path.empty()) {
- min_delta_seen = delta;
- icon_path = path;
- }
- }
- }
-
- if (!icon_path.empty() || !inherits || inherits_ == "")
- return icon_path;
-
- IconTheme* theme = LoadTheme(inherits_);
- // Inheriting from itself means the theme is buggy but we shouldn't crash.
- if (theme && theme != this)
- return theme->GetIconPath(icon_name, size, inherits);
- else
- return FilePath();
-}
-
-IconTheme* IconTheme::LoadTheme(const std::string& theme_name) {
- scoped_ptr<IconTheme> theme;
- MimeUtilConstants::IconThemeMap* icon_themes =
- &MimeUtilConstants::GetInstance()->icon_themes_;
- if (icon_themes->find(theme_name) != icon_themes->end()) {
- theme.reset((*icon_themes)[theme_name]);
- } else {
- theme.reset(new IconTheme(theme_name));
- if (!theme->IsValid())
- theme.reset();
- (*icon_themes)[theme_name] = theme.get();
- }
- return theme.release();
-}
-
-FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name,
- const std::string& subdir) {
- FilePath icon_path;
- std::list<FilePath>::iterator dir_iter;
- MimeUtilConstants::IconFormats* icon_formats =
- &MimeUtilConstants::GetInstance()->icon_formats_;
- for (dir_iter = dirs_.begin(); dir_iter != dirs_.end(); ++dir_iter) {
- for (size_t i = 0; i < icon_formats->size(); ++i) {
- icon_path = dir_iter->Append(subdir);
- icon_path = icon_path.Append(icon_name + (*icon_formats)[i]);
- if (PathExists(icon_path))
- return icon_path;
- }
- }
- return FilePath();
-}
-
-bool IconTheme::LoadIndexTheme(const FilePath& file) {
- FILE* fp = base::OpenFile(file, "r");
- SubDirInfo* current_info = NULL;
- if (!fp)
- return false;
-
- // Read entries.
- while (!feof(fp) && !ferror(fp)) {
- std::string buf = ReadLine(fp);
- if (buf == "")
- break;
-
- std::string entry;
- TrimWhitespaceASCII(buf, TRIM_ALL, &entry);
- if (entry.length() == 0 || entry[0] == '#') {
- // Blank line or Comment.
- continue;
- } else if (entry[0] == '[' && info_array_.get()) {
- current_info = NULL;
- std::string subdir = entry.substr(1, entry.length() - 2);
- if (subdirs_.find(subdir) != subdirs_.end())
- current_info = &info_array_[subdirs_[subdir]];
- }
-
- std::string key, value;
- std::vector<std::string> r;
- SplitStringDontTrim(entry, '=', &r);
- if (r.size() < 2)
- continue;
-
- TrimWhitespaceASCII(r[0], TRIM_ALL, &key);
- for (size_t i = 1; i < r.size(); i++)
- value.append(r[i]);
- TrimWhitespaceASCII(value, TRIM_ALL, &value);
-
- if (current_info) {
- if (key == "Size") {
- current_info->size = atoi(value.c_str());
- } else if (key == "Type") {
- if (value == "Fixed")
- current_info->type = SubDirInfo::Fixed;
- else if (value == "Scalable")
- current_info->type = SubDirInfo::Scalable;
- else if (value == "Threshold")
- current_info->type = SubDirInfo::Threshold;
- } else if (key == "MaxSize") {
- current_info->max_size = atoi(value.c_str());
- } else if (key == "MinSize") {
- current_info->min_size = atoi(value.c_str());
- } else if (key == "Threshold") {
- current_info->threshold = atoi(value.c_str());
- }
- } else {
- if (key.compare("Directories") == 0 && !info_array_.get()) {
- if (!SetDirectories(value)) break;
- } else if (key.compare("Inherits") == 0) {
- if (value != "hicolor")
- inherits_ = value;
- }
- }
- }
-
- base::CloseFile(fp);
- return info_array_.get() != NULL;
-}
-
-size_t IconTheme::MatchesSize(SubDirInfo* info, size_t size) {
- if (info->type == SubDirInfo::Fixed) {
- if (size > info->size)
- return size - info->size;
- else
- return info->size - size;
- } else if (info->type == SubDirInfo::Scalable) {
- if (size < info->min_size)
- return info->min_size - size;
- if (size > info->max_size)
- return size - info->max_size;
- return 0;
- } else {
- if (size + info->threshold < info->size)
- return info->size - size - info->threshold;
- if (size > info->size + info->threshold)
- return size - info->size - info->threshold;
- return 0;
- }
-}
-
-std::string IconTheme::ReadLine(FILE* fp) {
- if (!fp)
- return std::string();
-
- std::string result;
- const size_t kBufferSize = 100;
- char buffer[kBufferSize];
- while ((fgets(buffer, kBufferSize - 1, fp)) != NULL) {
- result += buffer;
- size_t len = result.length();
- if (len == 0)
- break;
- char end = result[len - 1];
- if (end == '\n' || end == '\0')
- break;
- }
-
- return result;
-}
-
-bool IconTheme::SetDirectories(const std::string& dirs) {
- int num = 0;
- std::string::size_type pos = 0, epos;
- std::string dir;
- while ((epos = dirs.find(',', pos)) != std::string::npos) {
- TrimWhitespaceASCII(dirs.substr(pos, epos - pos), TRIM_ALL, &dir);
- if (dir.length() == 0) {
- DLOG(WARNING) << "Invalid index.theme: blank subdir";
- return false;
- }
- subdirs_[dir] = num++;
- pos = epos + 1;
- }
- TrimWhitespaceASCII(dirs.substr(pos), TRIM_ALL, &dir);
- if (dir.length() == 0) {
- DLOG(WARNING) << "Invalid index.theme: blank subdir";
- return false;
- }
- subdirs_[dir] = num++;
- info_array_.reset(new SubDirInfo[num]);
- return true;
-}
-
-bool CheckDirExistsAndGetMtime(const FilePath& dir, Time* last_modified) {
- if (!DirectoryExists(dir))
- return false;
- PlatformFileInfo file_info;
- if (!GetFileInfo(dir, &file_info))
- return false;
- *last_modified = file_info.last_modified;
- return true;
-}
-
-// Make sure |dir| exists and add it to the list of icon directories.
-void TryAddIconDir(const FilePath& dir) {
- Time last_modified;
- if (!CheckDirExistsAndGetMtime(dir, &last_modified))
- return;
- MimeUtilConstants::GetInstance()->icon_dirs_[dir] = last_modified;
-}
-
-// For a xdg directory |dir|, add the appropriate icon sub-directories.
-void AddXDGDataDir(const FilePath& dir) {
- if (!DirectoryExists(dir))
- return;
- TryAddIconDir(dir.Append("icons"));
- TryAddIconDir(dir.Append("pixmaps"));
-}
-
-// Add all the xdg icon directories.
-void InitIconDir() {
- FilePath home = GetHomeDir();
- if (!home.empty()) {
- FilePath legacy_data_dir(home);
- legacy_data_dir = legacy_data_dir.AppendASCII(".icons");
- if (DirectoryExists(legacy_data_dir))
- TryAddIconDir(legacy_data_dir);
- }
- const char* env = getenv("XDG_DATA_HOME");
- if (env) {
- AddXDGDataDir(FilePath(env));
- } else if (!home.empty()) {
- FilePath local_data_dir(home);
- local_data_dir = local_data_dir.AppendASCII(".local");
- local_data_dir = local_data_dir.AppendASCII("share");
- AddXDGDataDir(local_data_dir);
- }
-
- env = getenv("XDG_DATA_DIRS");
- if (!env) {
- AddXDGDataDir(FilePath("/usr/local/share"));
- AddXDGDataDir(FilePath("/usr/share"));
- } else {
- std::string xdg_data_dirs = env;
- std::string::size_type pos = 0, epos;
- while ((epos = xdg_data_dirs.find(':', pos)) != std::string::npos) {
- AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos, epos - pos)));
- pos = epos + 1;
- }
- AddXDGDataDir(FilePath(xdg_data_dirs.substr(pos)));
- }
-}
-
-void EnsureUpdated() {
- MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
- if (constants->last_check_time_.is_null()) {
- constants->last_check_time_ = TimeTicks::Now();
- InitIconDir();
- return;
- }
-
- // Per xdg theme spec, we should check the icon directories every so often
- // for newly added icons.
- TimeDelta time_since_last_check =
- TimeTicks::Now() - constants->last_check_time_;
- if (time_since_last_check.InSeconds() > constants->kUpdateIntervalInSeconds) {
- constants->last_check_time_ += time_since_last_check;
-
- bool rescan_icon_dirs = false;
- MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
- MimeUtilConstants::IconDirMtimeMap::iterator iter;
- for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
- Time last_modified;
- if (!CheckDirExistsAndGetMtime(iter->first, &last_modified) ||
- last_modified != iter->second) {
- rescan_icon_dirs = true;
- break;
- }
- }
-
- if (rescan_icon_dirs) {
- constants->icon_dirs_.clear();
- constants->icon_themes_.clear();
- InitIconDir();
- }
- }
-}
-
-// Find a fallback icon if we cannot find it in the default theme.
-FilePath LookupFallbackIcon(const std::string& icon_name) {
- MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
- MimeUtilConstants::IconDirMtimeMap::iterator iter;
- MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
- MimeUtilConstants::IconFormats* icon_formats = &constants->icon_formats_;
- for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
- for (size_t i = 0; i < icon_formats->size(); ++i) {
- FilePath icon = iter->first.Append(icon_name + (*icon_formats)[i]);
- if (PathExists(icon))
- return icon;
- }
- }
- return FilePath();
-}
-
-// Initialize the list of default themes.
-void InitDefaultThemes() {
- IconTheme** default_themes =
- MimeUtilConstants::GetInstance()->default_themes_;
-
- scoped_ptr<Environment> env(Environment::Create());
- base::nix::DesktopEnvironment desktop_env =
- base::nix::GetDesktopEnvironment(env.get());
- if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
- desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE4) {
- // KDE
- std::string kde_default_theme;
- std::string kde_fallback_theme;
-
- // TODO(thestig): Figure out how to get the current icon theme on KDE.
- // Setting stored in ~/.kde/share/config/kdeglobals under Icons -> Theme.
- default_themes[0] = NULL;
-
- // Try some reasonable defaults for KDE.
- if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3) {
- // KDE 3
- kde_default_theme = "default.kde";
- kde_fallback_theme = "crystalsvg";
- } else {
- // KDE 4
- kde_default_theme = "default.kde4";
- kde_fallback_theme = "oxygen";
- }
- default_themes[1] = IconTheme::LoadTheme(kde_default_theme);
- default_themes[2] = IconTheme::LoadTheme(kde_fallback_theme);
- } else {
- // Assume it's Gnome and use GTK to figure out the theme.
- default_themes[1] = IconTheme::LoadTheme(
- MimeUtilConstants::GetInstance()->icon_theme_name_);
- default_themes[2] = IconTheme::LoadTheme("gnome");
- }
- // hicolor needs to be last per icon theme spec.
- default_themes[3] = IconTheme::LoadTheme("hicolor");
-
- for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
- if (default_themes[i] == NULL)
- continue;
- // NULL out duplicate pointers.
- for (size_t j = i + 1; j < MimeUtilConstants::kDefaultThemeNum; j++) {
- if (default_themes[j] == default_themes[i])
- default_themes[j] = NULL;
- }
- }
-}
-
-// Try to find an icon with the name |icon_name| that's |size| pixels.
-FilePath LookupIconInDefaultTheme(const std::string& icon_name, int size) {
- EnsureUpdated();
- MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
- MimeUtilConstants::IconThemeMap* icon_themes = &constants->icon_themes_;
- if (icon_themes->empty())
- InitDefaultThemes();
-
- FilePath icon_path;
- IconTheme** default_themes = constants->default_themes_;
- for (size_t i = 0; i < MimeUtilConstants::kDefaultThemeNum; i++) {
- if (default_themes[i]) {
- icon_path = default_themes[i]->GetIconPath(icon_name, size, true);
- if (!icon_path.empty())
- return icon_path;
- }
- }
- return LookupFallbackIcon(icon_name);
-}
-
-MimeUtilConstants::~MimeUtilConstants() {
- for (size_t i = 0; i < kDefaultThemeNum; i++)
- delete default_themes_[i];
-}
-
} // namespace
std::string GetFileMimeType(const FilePath& filepath) {
@@ -586,68 +35,5 @@ std::string GetDataMimeType(const std::string& data) {
return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL);
}
-void SetIconThemeName(const std::string& name) {
- // If the theme name is already loaded, do nothing. Chrome doesn't respond
- // to changes in the system theme, so we never need to set this more than
- // once.
- if (!MimeUtilConstants::GetInstance()->icon_theme_name_.empty())
- return;
-
- MimeUtilConstants::GetInstance()->icon_theme_name_ = name;
-}
-
-FilePath GetMimeIcon(const std::string& mime_type, size_t size) {
- ThreadRestrictions::AssertIOAllowed();
- std::vector<std::string> icon_names;
- std::string icon_name;
- FilePath icon_file;
-
- if (!mime_type.empty()) {
- AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
- const char *icon = xdg_mime_get_icon(mime_type.c_str());
- icon_name = std::string(icon ? icon : "");
- }
-
- if (icon_name.length())
- icon_names.push_back(icon_name);
-
- // For text/plain, try text-plain.
- icon_name = mime_type;
- for (size_t i = icon_name.find('/', 0); i != std::string::npos;
- i = icon_name.find('/', i + 1)) {
- icon_name[i] = '-';
- }
- icon_names.push_back(icon_name);
- // Also try gnome-mime-text-plain.
- icon_names.push_back("gnome-mime-" + icon_name);
-
- // Try "deb" for "application/x-deb" in KDE 3.
- size_t x_substr_pos = mime_type.find("/x-");
- if (x_substr_pos != std::string::npos) {
- icon_name = mime_type.substr(x_substr_pos + 3);
- icon_names.push_back(icon_name);
- }
-
- // Try generic name like text-x-generic.
- icon_name = mime_type.substr(0, mime_type.find('/')) + "-x-generic";
- icon_names.push_back(icon_name);
-
- // Last resort
- icon_names.push_back("unknown");
-
- for (size_t i = 0; i < icon_names.size(); i++) {
- if (icon_names[i][0] == '/') {
- icon_file = FilePath(icon_names[i]);
- if (PathExists(icon_file))
- return icon_file;
- } else {
- icon_file = LookupIconInDefaultTheme(icon_names[i], size);
- if (!icon_file.empty())
- return icon_file;
- }
- }
- return FilePath();
-}
-
} // namespace nix
} // namespace base
diff --git a/chromium/base/nix/mime_util_xdg.h b/chromium/base/nix/mime_util_xdg.h
index 79eb782e60a..e40415e1c48 100644
--- a/chromium/base/nix/mime_util_xdg.h
+++ b/chromium/base/nix/mime_util_xdg.h
@@ -25,17 +25,6 @@ BASE_EXPORT std::string GetFileMimeType(const FilePath& filepath);
// Get the mime type for a byte vector.
BASE_EXPORT std::string GetDataMimeType(const std::string& data);
-// Sets the current icon theme that we've detected from the desktop
-// environment. Currently only works when we believe we're in a GTK
-// environment.
-BASE_EXPORT void SetIconThemeName(const std::string& name);
-
-// Gets the file name for an icon given the mime type and icon pixel size.
-// Where an icon is a square image of |size| x |size|.
-// This will try to find the closest matching icon. If that's not available,
-// then a generic icon, and finally an empty FilePath if all else fails.
-BASE_EXPORT FilePath GetMimeIcon(const std::string& mime_type, size_t size);
-
} // namespace nix
} // namespace base
diff --git a/chromium/base/nix/xdg_util.cc b/chromium/base/nix/xdg_util.cc
index 4c1510f5c11..4086a3d0af1 100644
--- a/chromium/base/nix/xdg_util.cc
+++ b/chromium/base/nix/xdg_util.cc
@@ -6,9 +6,11 @@
#include <string>
+#include "base/base_paths.h"
#include "base/environment.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
+#include "base/path_service.h"
#include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h"
namespace {
@@ -28,10 +30,12 @@ FilePath GetXDGDirectory(Environment* env, const char* env_name,
const char* fallback_dir) {
FilePath path;
std::string env_value;
- if (env->GetVar(env_name, &env_value) && !env_value.empty())
+ if (env->GetVar(env_name, &env_value) && !env_value.empty()) {
path = FilePath(env_value);
- else
- path = GetHomeDir().Append(fallback_dir);
+ } else {
+ PathService::Get(base::DIR_HOME, &path);
+ path = path.Append(fallback_dir);
+ }
return path.StripTrailingSeparators();
}
@@ -42,7 +46,8 @@ FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) {
path = FilePath(xdg_dir);
free(xdg_dir);
} else {
- path = GetHomeDir().Append(fallback_dir);
+ PathService::Get(base::DIR_HOME, &path);
+ path = path.Append(fallback_dir);
}
return path.StripTrailingSeparators();
}
diff --git a/chromium/base/numerics/OWNERS b/chromium/base/numerics/OWNERS
new file mode 100644
index 00000000000..41f35fc79b4
--- /dev/null
+++ b/chromium/base/numerics/OWNERS
@@ -0,0 +1,3 @@
+jschuh@chromium.org
+tsepez@chromium.org
+
diff --git a/chromium/base/numerics/safe_conversions.h b/chromium/base/numerics/safe_conversions.h
new file mode 100644
index 00000000000..fe85fc6f52d
--- /dev/null
+++ b/chromium/base/numerics/safe_conversions.h
@@ -0,0 +1,64 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SAFE_CONVERSIONS_H_
+#define BASE_SAFE_CONVERSIONS_H_
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/numerics/safe_conversions_impl.h"
+
+namespace base {
+
+// Convenience function that returns true if the supplied value is in range
+// for the destination type.
+template <typename Dst, typename Src>
+inline bool IsValueInRangeForNumericType(Src value) {
+ return internal::DstRangeRelationToSrcRange<Dst>(value) ==
+ internal::RANGE_VALID;
+}
+
+// checked_cast<> is analogous to static_cast<> for numeric types,
+// except that it CHECKs that the specified numeric conversion will not
+// overflow or underflow. NaN source will always trigger a CHECK.
+template <typename Dst, typename Src>
+inline Dst checked_cast(Src value) {
+ CHECK(IsValueInRangeForNumericType<Dst>(value));
+ return static_cast<Dst>(value);
+}
+
+// saturated_cast<> is analogous to static_cast<> for numeric types, except
+// that the specified numeric conversion will saturate rather than overflow or
+// underflow. NaN assignment to an integral will trigger a CHECK condition.
+template <typename Dst, typename Src>
+inline Dst saturated_cast(Src value) {
+ // Optimization for floating point values, which already saturate.
+ if (std::numeric_limits<Dst>::is_iec559)
+ return static_cast<Dst>(value);
+
+ switch (internal::DstRangeRelationToSrcRange<Dst>(value)) {
+ case internal::RANGE_VALID:
+ return static_cast<Dst>(value);
+
+ case internal::RANGE_UNDERFLOW:
+ return std::numeric_limits<Dst>::min();
+
+ case internal::RANGE_OVERFLOW:
+ return std::numeric_limits<Dst>::max();
+
+ // Should fail only on attempting to assign NaN to a saturated integer.
+ case internal::RANGE_INVALID:
+ CHECK(false);
+ return std::numeric_limits<Dst>::max();
+ }
+
+ NOTREACHED();
+ return static_cast<Dst>(value);
+}
+
+} // namespace base
+
+#endif // BASE_SAFE_CONVERSIONS_H_
+
diff --git a/chromium/base/numerics/safe_conversions_impl.h b/chromium/base/numerics/safe_conversions_impl.h
new file mode 100644
index 00000000000..f05d553815e
--- /dev/null
+++ b/chromium/base/numerics/safe_conversions_impl.h
@@ -0,0 +1,217 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SAFE_CONVERSIONS_IMPL_H_
+#define BASE_SAFE_CONVERSIONS_IMPL_H_
+
+#include <limits>
+
+#include "base/macros.h"
+#include "base/template_util.h"
+
+namespace base {
+namespace internal {
+
+// The std library doesn't provide a binary max_exponent for integers, however
+// we can compute one by adding one to the number of non-sign bits. This allows
+// for accurate range comparisons between floating point and integer types.
+template <typename NumericType>
+struct MaxExponent {
+ static const int value = std::numeric_limits<NumericType>::is_iec559
+ ? std::numeric_limits<NumericType>::max_exponent
+ : (sizeof(NumericType) * 8 + 1 -
+ std::numeric_limits<NumericType>::is_signed);
+};
+
+enum IntegerRepresentation {
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED
+};
+
+// A range for a given nunmeric Src type is contained for a given numeric Dst
+// type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and
+// numeric_limits<Src>::min() >= numeric_limits<Dst>::min() are true.
+// We implement this as template specializations rather than simple static
+// comparisons to ensure type correctness in our comparisons.
+enum NumericRangeRepresentation {
+ NUMERIC_RANGE_NOT_CONTAINED,
+ NUMERIC_RANGE_CONTAINED
+};
+
+// Helper templates to statically determine if our destination type can contain
+// maximum and minimum values represented by the source type.
+
+template <
+ typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign =
+ std::numeric_limits<Src>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED >
+struct StaticDstRangeRelationToSrcRange;
+
+// Same sign: Dst is guaranteed to contain Src only if its range is equal or
+// larger.
+template <typename Dst, typename Src, IntegerRepresentation Sign>
+struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value >= MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Unsigned to signed: Dst is guaranteed to contain source only if its range is
+// larger.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED> {
+ static const NumericRangeRepresentation value =
+ MaxExponent<Dst>::value > MaxExponent<Src>::value
+ ? NUMERIC_RANGE_CONTAINED
+ : NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+// Signed to unsigned: Dst cannot be statically determined to contain Src.
+template <typename Dst, typename Src>
+struct StaticDstRangeRelationToSrcRange<Dst,
+ Src,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED> {
+ static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED;
+};
+
+enum RangeConstraint {
+ RANGE_VALID = 0x0, // Value can be represented by the destination type.
+ RANGE_UNDERFLOW = 0x1, // Value would overflow.
+ RANGE_OVERFLOW = 0x2, // Value would underflow.
+ RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN).
+};
+
+// Helper function for coercing an int back to a RangeContraint.
+inline RangeConstraint GetRangeConstraint(int integer_range_constraint) {
+ DCHECK(integer_range_constraint >= RANGE_VALID &&
+ integer_range_constraint <= RANGE_INVALID);
+ return static_cast<RangeConstraint>(integer_range_constraint);
+}
+
+// This function creates a RangeConstraint from an upper and lower bound
+// check by taking advantage of the fact that only NaN can be out of range in
+// both directions at once.
+inline RangeConstraint GetRangeConstraint(bool is_in_upper_bound,
+ bool is_in_lower_bound) {
+ return GetRangeConstraint((is_in_upper_bound ? 0 : RANGE_OVERFLOW) |
+ (is_in_lower_bound ? 0 : RANGE_UNDERFLOW));
+}
+
+template <
+ typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign = std::numeric_limits<Dst>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ IntegerRepresentation SrcSign = std::numeric_limits<Src>::is_signed
+ ? INTEGER_REPRESENTATION_SIGNED
+ : INTEGER_REPRESENTATION_UNSIGNED,
+ NumericRangeRepresentation DstRange =
+ StaticDstRangeRelationToSrcRange<Dst, Src>::value >
+struct DstRangeRelationToSrcRangeImpl;
+
+// The following templates are for ranges that must be verified at runtime. We
+// split it into checks based on signedness to avoid confusing casts and
+// compiler warnings on signed an unsigned comparisons.
+
+// Dst range is statically determined to contain Src: Nothing to check.
+template <typename Dst,
+ typename Src,
+ IntegerRepresentation DstSign,
+ IntegerRepresentation SrcSign>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ DstSign,
+ SrcSign,
+ NUMERIC_RANGE_CONTAINED> {
+ static RangeConstraint Check(Src value) { return RANGE_VALID; }
+};
+
+// Signed to signed narrowing: Both the upper and lower boundaries may be
+// exceeded.
+template <typename Dst, typename Src>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static RangeConstraint Check(Src value) {
+ return std::numeric_limits<Dst>::is_iec559
+ ? GetRangeConstraint(value <= std::numeric_limits<Dst>::max(),
+ value >= -std::numeric_limits<Dst>::max())
+ : GetRangeConstraint(value <= std::numeric_limits<Dst>::max(),
+ value >= std::numeric_limits<Dst>::min());
+ }
+};
+
+// Unsigned to unsigned narrowing: Only the upper boundary can be exceeded.
+template <typename Dst, typename Src>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static RangeConstraint Check(Src value) {
+ return GetRangeConstraint(value <= std::numeric_limits<Dst>::max(), true);
+ }
+};
+
+// Unsigned to signed: The upper boundary may be exceeded.
+template <typename Dst, typename Src>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ INTEGER_REPRESENTATION_SIGNED,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static RangeConstraint Check(Src value) {
+ return sizeof(Dst) > sizeof(Src)
+ ? RANGE_VALID
+ : GetRangeConstraint(
+ value <= static_cast<Src>(std::numeric_limits<Dst>::max()),
+ true);
+ }
+};
+
+// Signed to unsigned: The upper boundary may be exceeded for a narrower Dst,
+// and any negative value exceeds the lower boundary.
+template <typename Dst, typename Src>
+struct DstRangeRelationToSrcRangeImpl<Dst,
+ Src,
+ INTEGER_REPRESENTATION_UNSIGNED,
+ INTEGER_REPRESENTATION_SIGNED,
+ NUMERIC_RANGE_NOT_CONTAINED> {
+ static RangeConstraint Check(Src value) {
+ return (MaxExponent<Dst>::value >= MaxExponent<Src>::value)
+ ? GetRangeConstraint(true, value >= static_cast<Src>(0))
+ : GetRangeConstraint(
+ value <= static_cast<Src>(std::numeric_limits<Dst>::max()),
+ value >= static_cast<Src>(0));
+ }
+};
+
+template <typename Dst, typename Src>
+inline RangeConstraint DstRangeRelationToSrcRange(Src value) {
+ COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized,
+ argument_must_be_numeric);
+ COMPILE_ASSERT(std::numeric_limits<Dst>::is_specialized,
+ result_must_be_numeric);
+ return DstRangeRelationToSrcRangeImpl<Dst, Src>::Check(value);
+}
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_SAFE_CONVERSIONS_IMPL_H_
+
diff --git a/chromium/base/numerics/safe_math.h b/chromium/base/numerics/safe_math.h
new file mode 100644
index 00000000000..51a534f421c
--- /dev/null
+++ b/chromium/base/numerics/safe_math.h
@@ -0,0 +1,271 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SAFE_MATH_H_
+#define BASE_SAFE_MATH_H_
+
+#include "base/numerics/safe_math_impl.h"
+
+namespace base {
+
+namespace internal {
+
+// CheckedNumeric implements all the logic and operators for detecting integer
+// boundary conditions such as overflow, underflow, and invalid conversions.
+// The CheckedNumeric type implicitly converts from floating point and integer
+// data types, and contains overloads for basic arithmetic operations (i.e.: +,
+// -, *, /, %).
+//
+// The following methods convert from CheckedNumeric to standard numeric values:
+// IsValid() - Returns true if the underlying numeric value is valid (i.e. has
+// has not wrapped and is not the result of an invalid conversion).
+// ValueOrDie() - Returns the underlying value. If the state is not valid this
+// call will crash on a CHECK.
+// ValueOrDefault() - Returns the current value, or the supplied default if the
+// state is not valid.
+// ValueFloating() - Returns the underlying floating point value (valid only
+// only for floating point CheckedNumeric types).
+//
+// Bitwise operations are explicitly not supported, because correct
+// handling of some cases (e.g. sign manipulation) is ambiguous. Comparison
+// operations are explicitly not supported because they could result in a crash
+// on a CHECK condition. You should use patterns like the following for these
+// operations:
+// Bitwise operation:
+// CheckedNumeric<int> checked_int = untrusted_input_value;
+// int x = checked_int.ValueOrDefault(0) | kFlagValues;
+// Comparison:
+// CheckedNumeric<size_t> checked_size;
+// CheckedNumeric<int> checked_size = untrusted_input_value;
+// checked_size = checked_size + HEADER LENGTH;
+// if (checked_size.IsValid() && checked_size.ValueOrDie() < buffer_size)
+// Do stuff...
+template <typename T>
+class CheckedNumeric {
+ public:
+ typedef T type;
+
+ CheckedNumeric() {}
+
+ // Copy constructor.
+ template <typename Src>
+ CheckedNumeric(const CheckedNumeric<Src>& rhs)
+ : state_(rhs.ValueUnsafe(), rhs.validity()) {}
+
+ template <typename Src>
+ CheckedNumeric(Src value, RangeConstraint validity)
+ : state_(value, validity) {}
+
+ // This is not an explicit constructor because we implicitly upgrade regular
+ // numerics to CheckedNumerics to make them easier to use.
+ template <typename Src>
+ CheckedNumeric(Src value)
+ : state_(value) {
+ COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized,
+ argument_must_be_numeric);
+ }
+
+ // IsValid() is the public API to test if a CheckedNumeric is currently valid.
+ bool IsValid() const { return validity() == RANGE_VALID; }
+
+ // ValueOrDie() The primary accessor for the underlying value. If the current
+ // state is not valid it will CHECK and crash.
+ T ValueOrDie() const {
+ CHECK(IsValid());
+ return state_.value();
+ }
+
+ // ValueOrDefault(T default_value) A convenience method that returns the
+ // current value if the state is valid, and the supplied default_value for
+ // any other state.
+ T ValueOrDefault(T default_value) const {
+ return IsValid() ? state_.value() : default_value;
+ }
+
+ // ValueFloating() - Since floating point values include their validity state,
+ // we provide an easy method for extracting them directly, without a risk of
+ // crashing on a CHECK.
+ T ValueFloating() const {
+ COMPILE_ASSERT(std::numeric_limits<T>::is_iec559, argument_must_be_float);
+ return CheckedNumeric<T>::cast(*this).ValueUnsafe();
+ }
+
+ // validity() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now for
+ // tests and to avoid a big matrix of friend operator overloads. But the
+ // values it returns are likely to change in the future.
+ // Returns: current validity state (i.e. valid, overflow, underflow, nan).
+ // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
+ // saturation/wrapping so we can expose this state consistently and implement
+ // saturated arithmetic.
+ RangeConstraint validity() const { return state_.validity(); }
+
+ // ValueUnsafe() - DO NOT USE THIS IN EXTERNAL CODE - It is public right now
+ // for tests and to avoid a big matrix of friend operator overloads. But the
+ // values it returns are likely to change in the future.
+ // Returns: the raw numeric value, regardless of the current state.
+ // TODO(jschuh): crbug.com/332611 Figure out and implement semantics for
+ // saturation/wrapping so we can expose this state consistently and implement
+ // saturated arithmetic.
+ T ValueUnsafe() const { return state_.value(); }
+
+ // Prototypes for the supported arithmetic operator overloads.
+ template <typename Src> CheckedNumeric& operator+=(Src rhs);
+ template <typename Src> CheckedNumeric& operator-=(Src rhs);
+ template <typename Src> CheckedNumeric& operator*=(Src rhs);
+ template <typename Src> CheckedNumeric& operator/=(Src rhs);
+ template <typename Src> CheckedNumeric& operator%=(Src rhs);
+
+ CheckedNumeric operator-() const {
+ RangeConstraint validity;
+ T value = CheckedNeg(state_.value(), &validity);
+ // Negation is always valid for floating point.
+ if (std::numeric_limits<T>::is_iec559)
+ return CheckedNumeric<T>(value);
+
+ validity = GetRangeConstraint(state_.validity() | validity);
+ return CheckedNumeric<T>(value, validity);
+ }
+
+ CheckedNumeric Abs() const {
+ RangeConstraint validity;
+ T value = CheckedAbs(state_.value(), &validity);
+ // Absolute value is always valid for floating point.
+ if (std::numeric_limits<T>::is_iec559)
+ return CheckedNumeric<T>(value);
+
+ validity = GetRangeConstraint(state_.validity() | validity);
+ return CheckedNumeric<T>(value, validity);
+ }
+
+ CheckedNumeric& operator++() {
+ *this += 1;
+ return *this;
+ }
+
+ CheckedNumeric operator++(int) {
+ CheckedNumeric value = *this;
+ *this += 1;
+ return value;
+ }
+
+ CheckedNumeric& operator--() {
+ *this -= 1;
+ return *this;
+ }
+
+ CheckedNumeric operator--(int) {
+ CheckedNumeric value = *this;
+ *this -= 1;
+ return value;
+ }
+
+ // These static methods behave like a convenience cast operator targeting
+ // the desired CheckedNumeric type. As an optimization, a reference is
+ // returned when Src is the same type as T.
+ template <typename Src>
+ static CheckedNumeric<T> cast(
+ Src u,
+ typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
+ 0) {
+ return u;
+ }
+
+ template <typename Src>
+ static CheckedNumeric<T> cast(
+ const CheckedNumeric<Src>& u,
+ typename enable_if<!is_same<Src, T>::value, int>::type = 0) {
+ return u;
+ }
+
+ static const CheckedNumeric<T>& cast(const CheckedNumeric<T>& u) { return u; }
+
+ private:
+ CheckedNumericState<T> state_;
+};
+
+// This is the boilerplate for the standard arithmetic operator overloads. A
+// macro isn't the prettiest solution, but it beats rewriting these five times.
+// Some details worth noting are:
+// * We apply the standard arithmetic promotions.
+// * We skip range checks for floating points.
+// * We skip range checks for destination integers with sufficient range.
+// TODO(jschuh): extract these out into templates.
+#define BASE_NUMERIC_ARITHMETIC_OPERATORS(NAME, OP, COMPOUND_OP) \
+ /* Binary arithmetic operator for CheckedNumerics of the same type. */ \
+ template <typename T> \
+ CheckedNumeric<typename ArithmeticPromotion<T>::type> operator OP( \
+ const CheckedNumeric<T>& lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T>::type Promotion; \
+ /* Floating point always takes the fast path */ \
+ if (std::numeric_limits<T>::is_iec559) \
+ return CheckedNumeric<T>(lhs.ValueUnsafe() OP rhs.ValueUnsafe()); \
+ if (IsIntegerArithmeticSafe<Promotion, T, T>::value) \
+ return CheckedNumeric<Promotion>( \
+ lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
+ GetRangeConstraint(rhs.validity() | lhs.validity())); \
+ RangeConstraint validity = RANGE_VALID; \
+ T result = Checked##NAME(static_cast<Promotion>(lhs.ValueUnsafe()), \
+ static_cast<Promotion>(rhs.ValueUnsafe()), \
+ &validity); \
+ return CheckedNumeric<Promotion>( \
+ result, \
+ GetRangeConstraint(validity | lhs.validity() | rhs.validity())); \
+ } \
+ /* Assignment arithmetic operator implementation from CheckedNumeric. */ \
+ template <typename T> \
+ template <typename Src> \
+ CheckedNumeric<T>& CheckedNumeric<T>::operator COMPOUND_OP(Src rhs) { \
+ *this = CheckedNumeric<T>::cast(*this) OP CheckedNumeric<Src>::cast(rhs); \
+ return *this; \
+ } \
+ /* Binary arithmetic operator for CheckedNumeric of different type. */ \
+ template <typename T, typename Src> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ const CheckedNumeric<Src>& lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>( \
+ lhs.ValueUnsafe() OP rhs.ValueUnsafe(), \
+ GetRangeConstraint(rhs.validity() | lhs.validity())); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
+ } \
+ /* Binary arithmetic operator for left CheckedNumeric and right numeric. */ \
+ template <typename T, typename Src> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ const CheckedNumeric<T>& lhs, Src rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>(lhs.ValueUnsafe() OP rhs, \
+ lhs.validity()); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
+ } \
+ /* Binary arithmetic operator for right numeric and left CheckedNumeric. */ \
+ template <typename T, typename Src> \
+ CheckedNumeric<typename ArithmeticPromotion<T, Src>::type> operator OP( \
+ Src lhs, const CheckedNumeric<T>& rhs) { \
+ typedef typename ArithmeticPromotion<T, Src>::type Promotion; \
+ if (IsIntegerArithmeticSafe<Promotion, T, Src>::value) \
+ return CheckedNumeric<Promotion>(lhs OP rhs.ValueUnsafe(), \
+ rhs.validity()); \
+ return CheckedNumeric<Promotion>::cast(lhs) \
+ OP CheckedNumeric<Promotion>::cast(rhs); \
+ }
+
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Add, +, += )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Sub, -, -= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mul, *, *= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Div, /, /= )
+BASE_NUMERIC_ARITHMETIC_OPERATORS(Mod, %, %= )
+
+#undef BASE_NUMERIC_ARITHMETIC_OPERATORS
+
+} // namespace internal
+
+using internal::CheckedNumeric;
+
+} // namespace base
+
+#endif // BASE_SAFE_MATH_H_
diff --git a/chromium/base/numerics/safe_math_impl.h b/chromium/base/numerics/safe_math_impl.h
new file mode 100644
index 00000000000..3b5e64dd5b5
--- /dev/null
+++ b/chromium/base/numerics/safe_math_impl.h
@@ -0,0 +1,502 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SAFE_MATH_IMPL_H_
+#define SAFE_MATH_IMPL_H_
+
+#include <stdint.h>
+
+#include <cmath>
+#include <cstdlib>
+#include <limits>
+
+#include "base/macros.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/template_util.h"
+
+namespace base {
+namespace internal {
+
+// Everything from here up to the floating point operations is portable C++,
+// but it may not be fast. This code could be split based on
+// platform/architecture and replaced with potentially faster implementations.
+
+// Integer promotion templates used by the portable checked integer arithmetic.
+template <size_t Size, bool IsSigned>
+struct IntegerForSizeAndSign;
+template <>
+struct IntegerForSizeAndSign<1, true> {
+ typedef int8_t type;
+};
+template <>
+struct IntegerForSizeAndSign<1, false> {
+ typedef uint8_t type;
+};
+template <>
+struct IntegerForSizeAndSign<2, true> {
+ typedef int16_t type;
+};
+template <>
+struct IntegerForSizeAndSign<2, false> {
+ typedef uint16_t type;
+};
+template <>
+struct IntegerForSizeAndSign<4, true> {
+ typedef int32_t type;
+};
+template <>
+struct IntegerForSizeAndSign<4, false> {
+ typedef uint32_t type;
+};
+template <>
+struct IntegerForSizeAndSign<8, true> {
+ typedef int64_t type;
+};
+template <>
+struct IntegerForSizeAndSign<8, false> {
+ typedef uint64_t type;
+};
+
+// WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to
+// support 128-bit math, then the ArithmeticPromotion template below will need
+// to be updated (or more likely replaced with a decltype expression).
+
+template <typename Integer>
+struct UnsignedIntegerForSize {
+ typedef typename enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type;
+};
+
+template <typename Integer>
+struct SignedIntegerForSize {
+ typedef typename enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type;
+};
+
+template <typename Integer>
+struct TwiceWiderInteger {
+ typedef typename enable_if<
+ std::numeric_limits<Integer>::is_integer,
+ typename IntegerForSizeAndSign<
+ sizeof(Integer) * 2,
+ std::numeric_limits<Integer>::is_signed>::type>::type type;
+};
+
+template <typename Integer>
+struct PositionOfSignBit {
+ static const typename enable_if<std::numeric_limits<Integer>::is_integer,
+ size_t>::type value = 8 * sizeof(Integer) - 1;
+};
+
+// Helper templates for integer manipulations.
+
+template <typename T>
+bool HasSignBit(T x) {
+ // Cast to unsigned since right shift on signed is undefined.
+ return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >>
+ PositionOfSignBit<T>::value);
+}
+
+// This wrapper undoes the standard integer promotions.
+template <typename T>
+T BinaryComplement(T x) {
+ return ~x;
+}
+
+// Here are the actual portable checked integer math implementations.
+// TODO(jschuh): Break this code out from the enable_if pattern and find a clean
+// way to coalesce things into the CheckedNumericState specializations below.
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_integer, T>::type
+CheckedAdd(T x, T y, RangeConstraint* validity) {
+ // Since the value of x+y is undefined if we have a signed type, we compute
+ // it using the unsigned type of the same size.
+ typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = ux + uy;
+ // Addition is valid if the sign of (x + y) is equal to either that of x or
+ // that of y.
+ if (std::numeric_limits<T>::is_signed) {
+ if (HasSignBit(BinaryComplement((uresult ^ ux) & (uresult ^ uy))))
+ *validity = RANGE_VALID;
+ else // Direction of wrap is inverse of result sign.
+ *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
+
+ } else { // Unsigned is either valid or overflow.
+ *validity = BinaryComplement(x) >= y ? RANGE_VALID : RANGE_OVERFLOW;
+ }
+ return static_cast<T>(uresult);
+}
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_integer, T>::type
+CheckedSub(T x, T y, RangeConstraint* validity) {
+ // Since the value of x+y is undefined if we have a signed type, we compute
+ // it using the unsigned type of the same size.
+ typedef typename UnsignedIntegerForSize<T>::type UnsignedDst;
+ UnsignedDst ux = static_cast<UnsignedDst>(x);
+ UnsignedDst uy = static_cast<UnsignedDst>(y);
+ UnsignedDst uresult = ux - uy;
+ // Subtraction is valid if either x and y have same sign, or (x-y) and x have
+ // the same sign.
+ if (std::numeric_limits<T>::is_signed) {
+ if (HasSignBit(BinaryComplement((uresult ^ ux) & (ux ^ uy))))
+ *validity = RANGE_VALID;
+ else // Direction of wrap is inverse of result sign.
+ *validity = HasSignBit(uresult) ? RANGE_OVERFLOW : RANGE_UNDERFLOW;
+
+ } else { // Unsigned is either valid or underflow.
+ *validity = x >= y ? RANGE_VALID : RANGE_UNDERFLOW;
+ }
+ return static_cast<T>(uresult);
+}
+
+// Integer multiplication is a bit complicated. In the fast case we just
+// we just promote to a twice wider type, and range check the result. In the
+// slow case we need to manually check that the result won't be truncated by
+// checking with division against the appropriate bound.
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer && sizeof(T) * 2 <= sizeof(uintmax_t),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ typedef typename TwiceWiderInteger<T>::type IntermediateType;
+ IntermediateType tmp =
+ static_cast<IntermediateType>(x) * static_cast<IntermediateType>(y);
+ *validity = DstRangeRelationToSrcRange<T>(tmp);
+ return static_cast<T>(tmp);
+}
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_integer&& std::numeric_limits<
+ T>::is_signed&&(sizeof(T) * 2 > sizeof(uintmax_t)),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ // if either side is zero then the result will be zero.
+ if (!(x || y)) {
+ return RANGE_VALID;
+
+ } else if (x > 0) {
+ if (y > 0)
+ *validity =
+ x <= std::numeric_limits<T>::max() / y ? RANGE_VALID : RANGE_OVERFLOW;
+ else
+ *validity = y >= std::numeric_limits<T>::min() / x ? RANGE_VALID
+ : RANGE_UNDERFLOW;
+
+ } else {
+ if (y > 0)
+ *validity = x >= std::numeric_limits<T>::min() / y ? RANGE_VALID
+ : RANGE_UNDERFLOW;
+ else
+ *validity =
+ y >= std::numeric_limits<T>::max() / x ? RANGE_VALID : RANGE_OVERFLOW;
+ }
+
+ return x * y;
+}
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_integer &&
+ !std::numeric_limits<T>::is_signed &&
+ (sizeof(T) * 2 > sizeof(uintmax_t)),
+ T>::type
+CheckedMul(T x, T y, RangeConstraint* validity) {
+ *validity = (y == 0 || x <= std::numeric_limits<T>::max() / y)
+ ? RANGE_VALID
+ : RANGE_OVERFLOW;
+ return x * y;
+}
+
+// Division just requires a check for an invalid negation on signed min/-1.
+template <typename T>
+T CheckedDiv(
+ T x,
+ T y,
+ RangeConstraint* validity,
+ typename enable_if<std::numeric_limits<T>::is_integer, int>::type = 0) {
+ if (std::numeric_limits<T>::is_signed && x == std::numeric_limits<T>::min() &&
+ y == static_cast<T>(-1)) {
+ *validity = RANGE_OVERFLOW;
+ return std::numeric_limits<T>::min();
+ }
+
+ *validity = RANGE_VALID;
+ return x / y;
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedMod(T x, T y, RangeConstraint* validity) {
+ *validity = y > 0 ? RANGE_VALID : RANGE_INVALID;
+ return x % y;
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedMod(T x, T y, RangeConstraint* validity) {
+ *validity = RANGE_VALID;
+ return x % y;
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedNeg(T value, RangeConstraint* validity) {
+ *validity =
+ value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
+ // The negation of signed min is min, so catch that one.
+ return -value;
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedNeg(T value, RangeConstraint* validity) {
+ // The only legal unsigned negation is zero.
+ *validity = value ? RANGE_UNDERFLOW : RANGE_VALID;
+ return static_cast<T>(
+ -static_cast<typename SignedIntegerForSize<T>::type>(value));
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer&& std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedAbs(T value, RangeConstraint* validity) {
+ *validity =
+ value != std::numeric_limits<T>::min() ? RANGE_VALID : RANGE_OVERFLOW;
+ return std::abs(value);
+}
+
+template <typename T>
+typename enable_if<
+ std::numeric_limits<T>::is_integer && !std::numeric_limits<T>::is_signed,
+ T>::type
+CheckedAbs(T value, RangeConstraint* validity) {
+ // Absolute value of a positive is just its identiy.
+ *validity = RANGE_VALID;
+ return value;
+}
+
+// These are the floating point stubs that the compiler needs to see. Only the
+// negation operation is ever called.
+#define BASE_FLOAT_ARITHMETIC_STUBS(NAME) \
+ template <typename T> \
+ typename enable_if<std::numeric_limits<T>::is_iec559, T>::type \
+ Checked##NAME(T, T, RangeConstraint*) { \
+ NOTREACHED(); \
+ return 0; \
+ }
+
+BASE_FLOAT_ARITHMETIC_STUBS(Add)
+BASE_FLOAT_ARITHMETIC_STUBS(Sub)
+BASE_FLOAT_ARITHMETIC_STUBS(Mul)
+BASE_FLOAT_ARITHMETIC_STUBS(Div)
+BASE_FLOAT_ARITHMETIC_STUBS(Mod)
+
+#undef BASE_FLOAT_ARITHMETIC_STUBS
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedNeg(
+ T value,
+ RangeConstraint*) {
+ return -value;
+}
+
+template <typename T>
+typename enable_if<std::numeric_limits<T>::is_iec559, T>::type CheckedAbs(
+ T value,
+ RangeConstraint*) {
+ return std::abs(value);
+}
+
+// Floats carry around their validity state with them, but integers do not. So,
+// we wrap the underlying value in a specialization in order to hide that detail
+// and expose an interface via accessors.
+enum NumericRepresentation {
+ NUMERIC_INTEGER,
+ NUMERIC_FLOATING,
+ NUMERIC_UNKNOWN
+};
+
+template <typename NumericType>
+struct GetNumericRepresentation {
+ static const NumericRepresentation value =
+ std::numeric_limits<NumericType>::is_integer
+ ? NUMERIC_INTEGER
+ : (std::numeric_limits<NumericType>::is_iec559 ? NUMERIC_FLOATING
+ : NUMERIC_UNKNOWN);
+};
+
+template <typename T, NumericRepresentation type =
+ GetNumericRepresentation<T>::value>
+class CheckedNumericState {};
+
+// Integrals require quite a bit of additional housekeeping to manage state.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_INTEGER> {
+ private:
+ T value_;
+ RangeConstraint validity_;
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ CheckedNumericState() : value_(0), validity_(RANGE_VALID) {}
+
+ template <typename Src>
+ CheckedNumericState(Src value, RangeConstraint validity)
+ : value_(value),
+ validity_(GetRangeConstraint(validity |
+ DstRangeRelationToSrcRange<T>(value))) {
+ COMPILE_ASSERT(std::numeric_limits<Src>::is_specialized,
+ argument_must_be_numeric);
+ }
+
+ // Copy constructor.
+ template <typename Src>
+ CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(static_cast<T>(rhs.value())),
+ validity_(GetRangeConstraint(
+ rhs.validity() | DstRangeRelationToSrcRange<T>(rhs.value()))) {}
+
+ template <typename Src>
+ explicit CheckedNumericState(
+ Src value,
+ typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
+ 0)
+ : value_(static_cast<T>(value)),
+ validity_(DstRangeRelationToSrcRange<T>(value)) {}
+
+ RangeConstraint validity() const { return validity_; }
+ T value() const { return value_; }
+};
+
+// Floating points maintain their own validity, but need translation wrappers.
+template <typename T>
+class CheckedNumericState<T, NUMERIC_FLOATING> {
+ private:
+ T value_;
+
+ public:
+ template <typename Src, NumericRepresentation type>
+ friend class CheckedNumericState;
+
+ CheckedNumericState() : value_(0.0) {}
+
+ template <typename Src>
+ CheckedNumericState(
+ Src value,
+ RangeConstraint validity,
+ typename enable_if<std::numeric_limits<Src>::is_integer, int>::type = 0) {
+ switch (DstRangeRelationToSrcRange<T>(value)) {
+ case RANGE_VALID:
+ value_ = static_cast<T>(value);
+ break;
+
+ case RANGE_UNDERFLOW:
+ value_ = -std::numeric_limits<T>::infinity();
+ break;
+
+ case RANGE_OVERFLOW:
+ value_ = std::numeric_limits<T>::infinity();
+ break;
+
+ case RANGE_INVALID:
+ value_ = std::numeric_limits<T>::quiet_NaN();
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ }
+
+ template <typename Src>
+ explicit CheckedNumericState(
+ Src value,
+ typename enable_if<std::numeric_limits<Src>::is_specialized, int>::type =
+ 0)
+ : value_(static_cast<T>(value)) {}
+
+ // Copy constructor.
+ template <typename Src>
+ CheckedNumericState(const CheckedNumericState<Src>& rhs)
+ : value_(static_cast<T>(rhs.value())) {}
+
+ RangeConstraint validity() const {
+ return GetRangeConstraint(value_ <= std::numeric_limits<T>::max(),
+ value_ >= -std::numeric_limits<T>::max());
+ }
+ T value() const { return value_; }
+};
+
+// For integers less than 128-bit and floats 32-bit or larger, we can distil
+// C/C++ arithmetic promotions down to two simple rules:
+// 1. The type with the larger maximum exponent always takes precedence.
+// 2. The resulting type must be promoted to at least an int.
+// The following template specializations implement that promotion logic.
+enum ArithmeticPromotionCategory {
+ LEFT_PROMOTION,
+ RIGHT_PROMOTION,
+ DEFAULT_PROMOTION
+};
+
+template <typename Lhs,
+ typename Rhs = Lhs,
+ ArithmeticPromotionCategory Promotion =
+ (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value)
+ ? (MaxExponent<Lhs>::value > MaxExponent<int>::value
+ ? LEFT_PROMOTION
+ : DEFAULT_PROMOTION)
+ : (MaxExponent<Rhs>::value > MaxExponent<int>::value
+ ? RIGHT_PROMOTION
+ : DEFAULT_PROMOTION) >
+struct ArithmeticPromotion;
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, LEFT_PROMOTION> {
+ typedef Lhs type;
+};
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, RIGHT_PROMOTION> {
+ typedef Rhs type;
+};
+
+template <typename Lhs, typename Rhs>
+struct ArithmeticPromotion<Lhs, Rhs, DEFAULT_PROMOTION> {
+ typedef int type;
+};
+
+// We can statically check if operations on the provided types can wrap, so we
+// can skip the checked operations if they're not needed. So, for an integer we
+// care if the destination type preserves the sign and is twice the width of
+// the source.
+template <typename T, typename Lhs, typename Rhs>
+struct IsIntegerArithmeticSafe {
+ static const bool value = !std::numeric_limits<T>::is_iec559 &&
+ StaticDstRangeRelationToSrcRange<T, Lhs>::value ==
+ NUMERIC_RANGE_CONTAINED &&
+ sizeof(T) >= (2 * sizeof(Lhs)) &&
+ StaticDstRangeRelationToSrcRange<T, Rhs>::value !=
+ NUMERIC_RANGE_CONTAINED &&
+ sizeof(T) >= (2 * sizeof(Rhs));
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // SAFE_MATH_IMPL_H_
diff --git a/chromium/base/numerics/safe_numerics_unittest.cc b/chromium/base/numerics/safe_numerics_unittest.cc
new file mode 100644
index 00000000000..09ad1305a14
--- /dev/null
+++ b/chromium/base/numerics/safe_numerics_unittest.cc
@@ -0,0 +1,580 @@
+// Copyright 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.
+
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+#include <mmintrin.h>
+#endif
+#include <stdint.h>
+
+#include <limits>
+
+#include "base/compiler_specific.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/numerics/safe_math.h"
+#include "base/template_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::numeric_limits;
+using base::CheckedNumeric;
+using base::checked_cast;
+using base::saturated_cast;
+using base::internal::MaxExponent;
+using base::internal::RANGE_VALID;
+using base::internal::RANGE_INVALID;
+using base::internal::RANGE_OVERFLOW;
+using base::internal::RANGE_UNDERFLOW;
+using base::enable_if;
+
+// MSVS 2013 ia32 may not reset the FPU between calculations, and the test
+// framework masks the exceptions. So we just force a manual reset after NaN.
+inline void ResetFloatingPointUnit() {
+#if defined(COMPILER_MSVC) && defined(ARCH_CPU_32_BITS)
+ _mm_empty();
+#endif
+}
+
+// Helper macros to wrap displaying the conversion types and line numbers.
+#define TEST_EXPECTED_VALIDITY(expected, actual) \
+ EXPECT_EQ(expected, CheckedNumeric<Dst>(actual).validity()) \
+ << "Result test: Value " << +(actual).ValueUnsafe() << " as " << dst \
+ << " on line " << line;
+
+#define TEST_EXPECTED_VALUE(expected, actual) \
+ EXPECT_EQ(static_cast<Dst>(expected), \
+ CheckedNumeric<Dst>(actual).ValueUnsafe()) \
+ << "Result test: Value " << +((actual).ValueUnsafe()) << " as " << dst \
+ << " on line " << line;
+
+// Signed integer arithmetic.
+template <typename Dst>
+static void TestSpecializedArithmetic(
+ const char* dst,
+ int line,
+ typename enable_if<
+ numeric_limits<Dst>::is_integer&& numeric_limits<Dst>::is_signed,
+ int>::type = 0) {
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW,
+ -CheckedNumeric<Dst>(DstLimits::min()));
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()).Abs());
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
+
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::max()) + -1);
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) + -1);
+ TEST_EXPECTED_VALIDITY(
+ RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max());
+
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) - 1);
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()) - -1);
+ TEST_EXPECTED_VALIDITY(
+ RANGE_OVERFLOW,
+ CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max());
+ TEST_EXPECTED_VALIDITY(
+ RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max());
+
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) * 2);
+
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) / -1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(-1) / 2);
+
+ // Modulus is legal only for integers.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+ TEST_EXPECTED_VALUE(-1, CheckedNumeric<Dst>(-1) % 2);
+ TEST_EXPECTED_VALIDITY(RANGE_INVALID, CheckedNumeric<Dst>(-1) % -2);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
+ // Test all the different modulus combinations.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+ CheckedNumeric<Dst> checked_dst = 1;
+ TEST_EXPECTED_VALUE(0, checked_dst %= 1);
+}
+
+// Unsigned integer arithmetic.
+template <typename Dst>
+static void TestSpecializedArithmetic(
+ const char* dst,
+ int line,
+ typename enable_if<
+ numeric_limits<Dst>::is_integer && !numeric_limits<Dst>::is_signed,
+ int>::type = 0) {
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min()));
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()).Abs());
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) + -1);
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(DstLimits::min()) - 1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) * 2);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) / 2);
+
+ // Modulus is legal only for integers.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() % 1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) % 2);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(DstLimits::min()) % 2);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(DstLimits::max()) % 2);
+ // Test all the different modulus combinations.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 % CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) % 1);
+ CheckedNumeric<Dst> checked_dst = 1;
+ TEST_EXPECTED_VALUE(0, checked_dst %= 1);
+}
+
+// Floating point arithmetic.
+template <typename Dst>
+void TestSpecializedArithmetic(
+ const char* dst,
+ int line,
+ typename enable_if<numeric_limits<Dst>::is_iec559, int>::type = 0) {
+ typedef numeric_limits<Dst> DstLimits;
+ TEST_EXPECTED_VALIDITY(RANGE_VALID, -CheckedNumeric<Dst>(DstLimits::min()));
+
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()).Abs());
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(-1).Abs());
+
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()) + -1);
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::max()) + 1);
+ TEST_EXPECTED_VALIDITY(
+ RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(-DstLimits::max()) + -DstLimits::max());
+
+ TEST_EXPECTED_VALIDITY(
+ RANGE_OVERFLOW,
+ CheckedNumeric<Dst>(DstLimits::max()) - -DstLimits::max());
+ TEST_EXPECTED_VALIDITY(
+ RANGE_UNDERFLOW,
+ CheckedNumeric<Dst>(-DstLimits::max()) - DstLimits::max());
+
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()) * 2);
+
+ TEST_EXPECTED_VALUE(-0.5, CheckedNumeric<Dst>(-1.0) / 2);
+ EXPECT_EQ(static_cast<Dst>(1.0), CheckedNumeric<Dst>(1.0).ValueFloating());
+}
+
+// Generic arithmetic tests.
+template <typename Dst>
+static void TestArithmetic(const char* dst, int line) {
+ typedef numeric_limits<Dst> DstLimits;
+
+ EXPECT_EQ(true, CheckedNumeric<Dst>().IsValid());
+ EXPECT_EQ(false,
+ CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) *
+ DstLimits::max()).IsValid());
+ EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDie());
+ EXPECT_EQ(static_cast<Dst>(0), CheckedNumeric<Dst>().ValueOrDefault(1));
+ EXPECT_EQ(static_cast<Dst>(1),
+ CheckedNumeric<Dst>(CheckedNumeric<Dst>(DstLimits::max()) *
+ DstLimits::max()).ValueOrDefault(1));
+
+ // Test the operator combinations.
+ TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(2, 1 + CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(0, 1 - CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, 1 * CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, 1 / CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(2, CheckedNumeric<Dst>(1) + 1);
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>(1) - 1);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) * 1);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
+ CheckedNumeric<Dst> checked_dst = 1;
+ TEST_EXPECTED_VALUE(2, checked_dst += 1);
+ checked_dst = 1;
+ TEST_EXPECTED_VALUE(0, checked_dst -= 1);
+ checked_dst = 1;
+ TEST_EXPECTED_VALUE(1, checked_dst *= 1);
+ checked_dst = 1;
+ TEST_EXPECTED_VALUE(1, checked_dst /= 1);
+
+ // Generic negation.
+ TEST_EXPECTED_VALUE(0, -CheckedNumeric<Dst>());
+ TEST_EXPECTED_VALUE(-1, -CheckedNumeric<Dst>(1));
+ TEST_EXPECTED_VALUE(1, -CheckedNumeric<Dst>(-1));
+ TEST_EXPECTED_VALUE(static_cast<Dst>(DstLimits::max() * -1),
+ -CheckedNumeric<Dst>(DstLimits::max()));
+
+ // Generic absolute value.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>().Abs());
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1).Abs());
+ TEST_EXPECTED_VALUE(DstLimits::max(),
+ CheckedNumeric<Dst>(DstLimits::max()).Abs());
+
+ // Generic addition.
+ TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>() + 1));
+ TEST_EXPECTED_VALUE(2, (CheckedNumeric<Dst>(1) + 1));
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(-1) + 1));
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::min()) + 1);
+ TEST_EXPECTED_VALIDITY(
+ RANGE_OVERFLOW, CheckedNumeric<Dst>(DstLimits::max()) + DstLimits::max());
+
+ // Generic subtraction.
+ TEST_EXPECTED_VALUE(-1, (CheckedNumeric<Dst>() - 1));
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>(1) - 1));
+ TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) - 1));
+ TEST_EXPECTED_VALIDITY(RANGE_VALID,
+ CheckedNumeric<Dst>(DstLimits::max()) - 1);
+
+ // Generic multiplication.
+ TEST_EXPECTED_VALUE(0, (CheckedNumeric<Dst>() * 1));
+ TEST_EXPECTED_VALUE(1, (CheckedNumeric<Dst>(1) * 1));
+ TEST_EXPECTED_VALUE(-2, (CheckedNumeric<Dst>(-1) * 2));
+ TEST_EXPECTED_VALIDITY(
+ RANGE_OVERFLOW, CheckedNumeric<Dst>(DstLimits::max()) * DstLimits::max());
+
+ // Generic division.
+ TEST_EXPECTED_VALUE(0, CheckedNumeric<Dst>() / 1);
+ TEST_EXPECTED_VALUE(1, CheckedNumeric<Dst>(1) / 1);
+ TEST_EXPECTED_VALUE(DstLimits::min() / 2,
+ CheckedNumeric<Dst>(DstLimits::min()) / 2);
+ TEST_EXPECTED_VALUE(DstLimits::max() / 2,
+ CheckedNumeric<Dst>(DstLimits::max()) / 2);
+
+ TestSpecializedArithmetic<Dst>(dst, line);
+}
+
+// Helper macro to wrap displaying the conversion types and line numbers.
+#define TEST_ARITHMETIC(Dst) TestArithmetic<Dst>(#Dst, __LINE__)
+
+TEST(SafeNumerics, SignedIntegerMath) {
+ TEST_ARITHMETIC(int8_t);
+ TEST_ARITHMETIC(int);
+ TEST_ARITHMETIC(intptr_t);
+ TEST_ARITHMETIC(intmax_t);
+}
+
+TEST(SafeNumerics, UnsignedIntegerMath) {
+ TEST_ARITHMETIC(uint8_t);
+ TEST_ARITHMETIC(unsigned int);
+ TEST_ARITHMETIC(uintptr_t);
+ TEST_ARITHMETIC(uintmax_t);
+}
+
+TEST(SafeNumerics, FloatingPointMath) {
+ TEST_ARITHMETIC(float);
+ TEST_ARITHMETIC(double);
+}
+
+// Enumerates the five different conversions types we need to test.
+enum NumericConversionType {
+ SIGN_PRESERVING_VALUE_PRESERVING,
+ SIGN_PRESERVING_NARROW,
+ SIGN_TO_UNSIGN_WIDEN_OR_EQUAL,
+ SIGN_TO_UNSIGN_NARROW,
+ UNSIGN_TO_SIGN_NARROW_OR_EQUAL,
+};
+
+// Template covering the different conversion tests.
+template <typename Dst, typename Src, NumericConversionType conversion>
+struct TestNumericConversion {};
+
+// EXPECT_EQ wrappers providing specific detail on test failures.
+#define TEST_EXPECTED_RANGE(expected, actual) \
+ EXPECT_EQ(expected, base::internal::DstRangeRelationToSrcRange<Dst>(actual)) \
+ << "Conversion test: " << src << " value " << actual << " to " << dst \
+ << " on line " << line;
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_VALUE_PRESERVING> {
+ static void Test(const char *dst, const char *src, int line) {
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ // Integral to floating.
+ COMPILE_ASSERT((DstLimits::is_iec559 && SrcLimits::is_integer) ||
+ // Not floating to integral and...
+ (!(DstLimits::is_integer && SrcLimits::is_iec559) &&
+ // Same sign, same numeric, source is narrower or same.
+ ((SrcLimits::is_signed == DstLimits::is_signed &&
+ sizeof(Dst) >= sizeof(Src)) ||
+ // Or signed destination and source is smaller
+ (DstLimits::is_signed && sizeof(Dst) > sizeof(Src)))),
+ comparison_must_be_sign_preserving_and_value_preserving);
+
+ const CheckedNumeric<Dst> checked_dst = SrcLimits::max();
+ ;
+ TEST_EXPECTED_VALIDITY(RANGE_VALID, checked_dst);
+ if (MaxExponent<Dst>::value > MaxExponent<Src>::value) {
+ if (MaxExponent<Dst>::value >= MaxExponent<Src>::value * 2 - 1) {
+ // At least twice larger type.
+ TEST_EXPECTED_VALIDITY(RANGE_VALID, SrcLimits::max() * checked_dst);
+
+ } else { // Larger, but not at least twice as large.
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, SrcLimits::max() * checked_dst);
+ TEST_EXPECTED_VALIDITY(RANGE_VALID, checked_dst + 1);
+ }
+ } else { // Same width type.
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + 1);
+ }
+
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+ if (SrcLimits::is_iec559) {
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max() * static_cast<Src>(-1));
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+ TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+ ResetFloatingPointUnit();
+ } else if (numeric_limits<Src>::is_signed) {
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+ }
+ }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_PRESERVING_NARROW> {
+ static void Test(const char *dst, const char *src, int line) {
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ COMPILE_ASSERT(SrcLimits::is_signed == DstLimits::is_signed,
+ destination_and_source_sign_must_be_the_same);
+ COMPILE_ASSERT(sizeof(Dst) < sizeof(Src) ||
+ (DstLimits::is_integer && SrcLimits::is_iec559),
+ destination_must_be_narrower_than_source);
+
+ const CheckedNumeric<Dst> checked_dst;
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst - SrcLimits::max());
+
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+ if (SrcLimits::is_iec559) {
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+ TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+ ResetFloatingPointUnit();
+ } else if (SrcLimits::is_signed) {
+ TEST_EXPECTED_VALUE(-1, checked_dst - static_cast<Src>(1));
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(-1));
+ } else {
+ TEST_EXPECTED_VALIDITY(RANGE_INVALID, checked_dst - static_cast<Src>(1));
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+ }
+ }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL> {
+ static void Test(const char *dst, const char *src, int line) {
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ COMPILE_ASSERT(sizeof(Dst) >= sizeof(Src),
+ destination_must_be_equal_or_wider_than_source);
+ COMPILE_ASSERT(SrcLimits::is_signed, source_must_be_signed);
+ COMPILE_ASSERT(!DstLimits::is_signed, destination_must_be_unsigned);
+
+ const CheckedNumeric<Dst> checked_dst;
+ TEST_EXPECTED_VALUE(SrcLimits::max(), checked_dst + SrcLimits::max());
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + static_cast<Src>(-1));
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + -SrcLimits::max());
+
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::max());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
+ }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, SIGN_TO_UNSIGN_NARROW> {
+ static void Test(const char *dst, const char *src, int line) {
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ COMPILE_ASSERT((DstLimits::is_integer && SrcLimits::is_iec559) ||
+ (sizeof(Dst) < sizeof(Src)),
+ destination_must_be_narrower_than_source);
+ COMPILE_ASSERT(SrcLimits::is_signed, source_must_be_signed);
+ COMPILE_ASSERT(!DstLimits::is_signed, destination_must_be_unsigned);
+
+ const CheckedNumeric<Dst> checked_dst;
+ TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max());
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + static_cast<Src>(-1));
+ TEST_EXPECTED_VALIDITY(RANGE_UNDERFLOW, checked_dst + -SrcLimits::max());
+
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, static_cast<Src>(-1));
+ if (SrcLimits::is_iec559) {
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::max() * -1);
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::infinity());
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::infinity() * -1);
+ TEST_EXPECTED_RANGE(RANGE_INVALID, SrcLimits::quiet_NaN());
+ ResetFloatingPointUnit();
+ } else {
+ TEST_EXPECTED_RANGE(RANGE_UNDERFLOW, SrcLimits::min());
+ }
+ }
+};
+
+template <typename Dst, typename Src>
+struct TestNumericConversion<Dst, Src, UNSIGN_TO_SIGN_NARROW_OR_EQUAL> {
+ static void Test(const char *dst, const char *src, int line) {
+ typedef numeric_limits<Src> SrcLimits;
+ typedef numeric_limits<Dst> DstLimits;
+ COMPILE_ASSERT(sizeof(Dst) <= sizeof(Src),
+ destination_must_be_narrower_or_equal_to_source);
+ COMPILE_ASSERT(!SrcLimits::is_signed, source_must_be_unsigned);
+ COMPILE_ASSERT(DstLimits::is_signed, destination_must_be_signed);
+
+ const CheckedNumeric<Dst> checked_dst;
+ TEST_EXPECTED_VALUE(1, checked_dst + static_cast<Src>(1));
+ TEST_EXPECTED_VALIDITY(RANGE_OVERFLOW, checked_dst + SrcLimits::max());
+ TEST_EXPECTED_VALUE(SrcLimits::min(), checked_dst + SrcLimits::min());
+
+ TEST_EXPECTED_RANGE(RANGE_VALID, SrcLimits::min());
+ TEST_EXPECTED_RANGE(RANGE_OVERFLOW, SrcLimits::max());
+ TEST_EXPECTED_RANGE(RANGE_VALID, static_cast<Src>(1));
+ }
+};
+
+// Helper macro to wrap displaying the conversion types and line numbers
+#define TEST_NUMERIC_CONVERSION(d, s, t) \
+ TestNumericConversion<d, s, t>::Test(#d, #s, __LINE__)
+
+TEST(SafeNumerics, IntMinOperations) {
+ TEST_NUMERIC_CONVERSION(int8_t, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(uint8_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+ TEST_NUMERIC_CONVERSION(int8_t, int, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(uint8_t, unsigned int, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(int8_t, float, SIGN_PRESERVING_NARROW);
+
+ TEST_NUMERIC_CONVERSION(uint8_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+ TEST_NUMERIC_CONVERSION(uint8_t, int, SIGN_TO_UNSIGN_NARROW);
+ TEST_NUMERIC_CONVERSION(uint8_t, intmax_t, SIGN_TO_UNSIGN_NARROW);
+ TEST_NUMERIC_CONVERSION(uint8_t, float, SIGN_TO_UNSIGN_NARROW);
+
+ TEST_NUMERIC_CONVERSION(int8_t, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+ TEST_NUMERIC_CONVERSION(int8_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, IntOperations) {
+ TEST_NUMERIC_CONVERSION(int, int, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(unsigned int, unsigned int,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(int, int8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(unsigned int, uint8_t,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(int, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+ TEST_NUMERIC_CONVERSION(int, intmax_t, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(unsigned int, uintmax_t, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(int, float, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(int, double, SIGN_PRESERVING_NARROW);
+
+ TEST_NUMERIC_CONVERSION(unsigned int, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+ TEST_NUMERIC_CONVERSION(unsigned int, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+ TEST_NUMERIC_CONVERSION(unsigned int, intmax_t, SIGN_TO_UNSIGN_NARROW);
+ TEST_NUMERIC_CONVERSION(unsigned int, float, SIGN_TO_UNSIGN_NARROW);
+ TEST_NUMERIC_CONVERSION(unsigned int, double, SIGN_TO_UNSIGN_NARROW);
+
+ TEST_NUMERIC_CONVERSION(int, unsigned int, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+ TEST_NUMERIC_CONVERSION(int, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, IntMaxOperations) {
+ TEST_NUMERIC_CONVERSION(intmax_t, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(uintmax_t, uintmax_t,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(intmax_t, int, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(uintmax_t, unsigned int,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(intmax_t, unsigned int,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(intmax_t, uint8_t, SIGN_PRESERVING_VALUE_PRESERVING);
+
+ TEST_NUMERIC_CONVERSION(intmax_t, float, SIGN_PRESERVING_NARROW);
+ TEST_NUMERIC_CONVERSION(intmax_t, double, SIGN_PRESERVING_NARROW);
+
+ TEST_NUMERIC_CONVERSION(uintmax_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+ TEST_NUMERIC_CONVERSION(uintmax_t, int8_t, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+
+ TEST_NUMERIC_CONVERSION(uintmax_t, float, SIGN_TO_UNSIGN_NARROW);
+ TEST_NUMERIC_CONVERSION(uintmax_t, double, SIGN_TO_UNSIGN_NARROW);
+
+ TEST_NUMERIC_CONVERSION(intmax_t, uintmax_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, FloatOperations) {
+ TEST_NUMERIC_CONVERSION(float, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(float, uintmax_t,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(float, int, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(float, unsigned int,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+
+ TEST_NUMERIC_CONVERSION(float, double, SIGN_PRESERVING_NARROW);
+}
+
+TEST(SafeNumerics, DoubleOperations) {
+ TEST_NUMERIC_CONVERSION(double, intmax_t, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(double, uintmax_t,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(double, int, SIGN_PRESERVING_VALUE_PRESERVING);
+ TEST_NUMERIC_CONVERSION(double, unsigned int,
+ SIGN_PRESERVING_VALUE_PRESERVING);
+}
+
+TEST(SafeNumerics, SizeTOperations) {
+ TEST_NUMERIC_CONVERSION(size_t, int, SIGN_TO_UNSIGN_WIDEN_OR_EQUAL);
+ TEST_NUMERIC_CONVERSION(int, size_t, UNSIGN_TO_SIGN_NARROW_OR_EQUAL);
+}
+
+TEST(SafeNumerics, CastTests) {
+// MSVC catches and warns that we're forcing saturation in these tests.
+// Since that's intentional, we need to shut this warning off.
+#if defined(COMPILER_MSVC)
+#pragma warning(disable : 4756)
+#endif
+
+ int small_positive = 1;
+ int small_negative = -1;
+ double double_small = 1.0;
+ double double_large = numeric_limits<double>::max();
+ double double_infinity = numeric_limits<float>::infinity();
+
+ // Just test that the cast compiles, since the other tests cover logic.
+ EXPECT_EQ(0, checked_cast<int>(static_cast<size_t>(0)));
+
+ // Test various saturation corner cases.
+ EXPECT_EQ(saturated_cast<int>(small_negative),
+ static_cast<int>(small_negative));
+ EXPECT_EQ(saturated_cast<int>(small_positive),
+ static_cast<int>(small_positive));
+ EXPECT_EQ(saturated_cast<unsigned>(small_negative),
+ static_cast<unsigned>(0));
+ EXPECT_EQ(saturated_cast<int>(double_small),
+ static_cast<int>(double_small));
+ EXPECT_EQ(saturated_cast<int>(double_large), numeric_limits<int>::max());
+ EXPECT_EQ(saturated_cast<float>(double_large), double_infinity);
+ EXPECT_EQ(saturated_cast<float>(-double_large), -double_infinity);
+}
+
diff --git a/chromium/base/observer_list_unittest.cc b/chromium/base/observer_list_unittest.cc
index 57843f401fc..1bda3dcadae 100644
--- a/chromium/base/observer_list_unittest.cc
+++ b/chromium/base/observer_list_unittest.cc
@@ -443,7 +443,7 @@ TEST(ObserverListTest, Existing) {
EXPECT_TRUE(b.added);
// B's adder should not have been notified because it was added during
- // notificaiton.
+ // notification.
EXPECT_EQ(0, b.adder.total);
// Notify again to make sure b's adder is notified.
@@ -467,7 +467,7 @@ TEST(ObserverListThreadSafeTest, Existing) {
EXPECT_TRUE(b.added);
// B's adder should not have been notified because it was added during
- // notificaiton.
+ // notification.
EXPECT_EQ(0, b.adder.total);
// Notify again to make sure b's adder is notified.
diff --git a/chromium/base/os_compat_android.cc b/chromium/base/os_compat_android.cc
index ec221e480dc..b2756b2b226 100644
--- a/chromium/base/os_compat_android.cc
+++ b/chromium/base/os_compat_android.cc
@@ -9,7 +9,10 @@
#include <math.h>
#include <sys/stat.h>
#include <sys/syscall.h>
+
+#if !defined(__LP64__)
#include <time64.h>
+#endif
#include "base/rand_util.h"
#include "base/strings/string_piece.h"
@@ -37,17 +40,19 @@ int futimes(int fd, const struct timeval tv[2]) {
return syscall(__NR_utimensat, fd, NULL, ts, 0);
}
-// Android has only timegm64() and no timegm().
+#if !defined(__LP64__)
+// 32-bit Android has only timegm64() and not timegm().
// We replicate the behaviour of timegm() when the result overflows time_t.
time_t timegm(struct tm* const t) {
// time_t is signed on Android.
- static const time_t kTimeMax = ~(1 << (sizeof(time_t) * CHAR_BIT - 1));
- static const time_t kTimeMin = (1 << (sizeof(time_t) * CHAR_BIT - 1));
+ static const time_t kTimeMax = ~(1L << (sizeof(time_t) * CHAR_BIT - 1));
+ static const time_t kTimeMin = (1L << (sizeof(time_t) * CHAR_BIT - 1));
time64_t result = timegm64(t);
if (result < kTimeMin || result > kTimeMax)
return -1;
return result;
}
+#endif
// The following is only needed when building with GCC 4.6 or higher
// (i.e. not with Android GCC 4.4.3, nor with Clang).
diff --git a/chromium/base/path_service.cc b/chromium/base/path_service.cc
index f0a6a844158..75833db9a11 100644
--- a/chromium/base/path_service.cc
+++ b/chromium/base/path_service.cc
@@ -187,7 +187,7 @@ bool PathService::Get(int key, FilePath* result) {
// special case the current directory because it can never be cached
if (key == base::DIR_CURRENT)
- return file_util::GetCurrentDirectory(result);
+ return base::GetCurrentDirectory(result);
Provider* provider = NULL;
{
@@ -233,13 +233,15 @@ bool PathService::Get(int key, FilePath* result) {
// static
bool PathService::Override(int key, const FilePath& path) {
- // Just call the full function with true for the value of |create|.
- return OverrideAndCreateIfNeeded(key, path, true);
+ // Just call the full function with true for the value of |create|, and
+ // assume that |path| may not be absolute yet.
+ return OverrideAndCreateIfNeeded(key, path, false, true);
}
// static
bool PathService::OverrideAndCreateIfNeeded(int key,
const FilePath& path,
+ bool is_absolute,
bool create) {
PathData* path_data = GetPathData();
DCHECK(path_data);
@@ -259,9 +261,12 @@ bool PathService::OverrideAndCreateIfNeeded(int key,
}
// We need to have an absolute path.
- file_path = MakeAbsoluteFilePath(file_path);
- if (file_path.empty())
- return false;
+ if (!is_absolute) {
+ file_path = MakeAbsoluteFilePath(file_path);
+ if (file_path.empty())
+ return false;
+ }
+ DCHECK(file_path.IsAbsolute());
base::AutoLock scoped_lock(path_data->lock);
diff --git a/chromium/base/path_service.h b/chromium/base/path_service.h
index 832b92b1581..554eb9e3464 100644
--- a/chromium/base/path_service.h
+++ b/chromium/base/path_service.h
@@ -41,13 +41,23 @@ class BASE_EXPORT PathService {
//
// WARNING: Consumers of PathService::Get may expect paths to be constant
// over the lifetime of the app, so this method should be used with caution.
+ //
+ // Unit tests generally should use ScopedPathOverride instead. Overrides from
+ // one test should not carry over to another.
static bool Override(int key, const base::FilePath& path);
- // This function does the same as PathService::Override but it takes an extra
- // parameter |create| which guides whether the directory to be overriden must
+ // This function does the same as PathService::Override but it takes extra
+ // parameters:
+ // - |is_absolute| indicates that |path| has already been expanded into an
+ // absolute path, otherwise MakeAbsoluteFilePath() will be used. This is
+ // useful to override paths that may not exist yet, since MakeAbsoluteFilePath
+ // fails for those. Note that MakeAbsoluteFilePath also expands symbolic
+ // links, even if path.IsAbsolute() is already true.
+ // - |create| guides whether the directory to be overriden must
// be created in case it doesn't exist already.
static bool OverrideAndCreateIfNeeded(int key,
const base::FilePath& path,
+ bool is_absolute,
bool create);
// To extend the set of supported keys, you can register a path provider,
diff --git a/chromium/base/path_service_unittest.cc b/chromium/base/path_service_unittest.cc
index fbb2d766deb..7a70a89c41a 100644
--- a/chromium/base/path_service_unittest.cc
+++ b/chromium/base/path_service_unittest.cc
@@ -100,8 +100,9 @@ typedef PlatformTest PathServiceTest;
TEST_F(PathServiceTest, Get) {
for (int key = base::PATH_START + 1; key < base::PATH_END; ++key) {
#if defined(OS_ANDROID)
- if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP)
- continue; // Android doesn't implement FILE_MODULE and DIR_USER_DESKTOP;
+ if (key == base::FILE_MODULE || key == base::DIR_USER_DESKTOP ||
+ key == base::DIR_HOME)
+ continue; // Android doesn't implement these.
#elif defined(OS_IOS)
if (key == base::DIR_USER_DESKTOP)
continue; // iOS doesn't implement DIR_USER_DESKTOP;
@@ -146,7 +147,7 @@ TEST_F(PathServiceTest, Get) {
#endif
}
-// test that all versions of the Override function of PathService do what they
+// Test that all versions of the Override function of PathService do what they
// are supposed to do.
TEST_F(PathServiceTest, Override) {
int my_special_key = 666;
@@ -162,12 +163,41 @@ TEST_F(PathServiceTest, Override) {
// PathService::OverrideAndCreateIfNeeded should obey the |create| parameter.
PathService::OverrideAndCreateIfNeeded(my_special_key,
fake_cache_dir2,
+ false,
false);
EXPECT_FALSE(base::PathExists(fake_cache_dir2));
EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
fake_cache_dir2,
+ false,
true));
EXPECT_TRUE(base::PathExists(fake_cache_dir2));
+
+#if defined(OS_POSIX)
+ base::FilePath non_existent(
+ base::MakeAbsoluteFilePath(temp_dir.path()).AppendASCII("non_existent"));
+ EXPECT_TRUE(non_existent.IsAbsolute());
+ EXPECT_FALSE(base::PathExists(non_existent));
+#if !defined(OS_ANDROID)
+ // This fails because MakeAbsoluteFilePath fails for non-existent files.
+ // Earlier versions of Bionic libc don't fail for non-existent files, so
+ // skip this check on Android.
+ EXPECT_FALSE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+ non_existent,
+ false,
+ false));
+#endif
+ // This works because indicating that |non_existent| is absolute skips the
+ // internal MakeAbsoluteFilePath call.
+ EXPECT_TRUE(PathService::OverrideAndCreateIfNeeded(my_special_key,
+ non_existent,
+ true,
+ false));
+ // Check that the path has been overridden and no directory was created.
+ EXPECT_FALSE(base::PathExists(non_existent));
+ base::FilePath path;
+ EXPECT_TRUE(PathService::Get(my_special_key, &path));
+ EXPECT_EQ(non_existent, path);
+#endif
}
// Check if multiple overrides can co-exist.
@@ -178,12 +208,12 @@ TEST_F(PathServiceTest, OverrideMultiple) {
base::FilePath fake_cache_dir1(temp_dir.path().AppendASCII("1"));
EXPECT_TRUE(PathService::Override(my_special_key, fake_cache_dir1));
EXPECT_TRUE(base::PathExists(fake_cache_dir1));
- ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1));
+ ASSERT_EQ(1, base::WriteFile(fake_cache_dir1.AppendASCII("t1"), ".", 1));
base::FilePath fake_cache_dir2(temp_dir.path().AppendASCII("2"));
EXPECT_TRUE(PathService::Override(my_special_key + 1, fake_cache_dir2));
EXPECT_TRUE(base::PathExists(fake_cache_dir2));
- ASSERT_EQ(1, file_util::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1));
+ ASSERT_EQ(1, base::WriteFile(fake_cache_dir2.AppendASCII("t2"), ".", 1));
base::FilePath result;
EXPECT_TRUE(PathService::Get(my_special_key, &result));
diff --git a/chromium/base/pickle.cc b/chromium/base/pickle.cc
index 12a32378788..a9e9a0fdfbc 100644
--- a/chromium/base/pickle.cc
+++ b/chromium/base/pickle.cc
@@ -19,8 +19,9 @@ const int Pickle::kPayloadUnit = 64;
static const size_t kCapacityReadOnly = static_cast<size_t>(-1);
PickleIterator::PickleIterator(const Pickle& pickle)
- : read_ptr_(pickle.payload()),
- read_end_ptr_(pickle.end_of_payload()) {
+ : payload_(pickle.payload()),
+ read_index_(0),
+ end_index_(pickle.payload_size()) {
}
template <typename Type>
@@ -35,28 +36,40 @@ inline bool PickleIterator::ReadBuiltinType(Type* result) {
return true;
}
+inline void PickleIterator::Advance(size_t size) {
+ size_t aligned_size = AlignInt(size, sizeof(uint32_t));
+ if (end_index_ - read_index_ < aligned_size) {
+ read_index_ = end_index_;
+ } else {
+ read_index_ += aligned_size;
+ }
+}
+
template<typename Type>
inline const char* PickleIterator::GetReadPointerAndAdvance() {
- const char* current_read_ptr = read_ptr_;
- if (read_ptr_ + sizeof(Type) > read_end_ptr_)
+ if (sizeof(Type) > end_index_ - read_index_) {
+ read_index_ = end_index_;
return NULL;
- if (sizeof(Type) < sizeof(uint32))
- read_ptr_ += AlignInt(sizeof(Type), sizeof(uint32));
- else
- read_ptr_ += sizeof(Type);
+ }
+ const char* current_read_ptr = payload_ + read_index_;
+ Advance(sizeof(Type));
return current_read_ptr;
}
const char* PickleIterator::GetReadPointerAndAdvance(int num_bytes) {
- if (num_bytes < 0 || read_end_ptr_ - read_ptr_ < num_bytes)
+ if (num_bytes < 0 ||
+ end_index_ - read_index_ < static_cast<size_t>(num_bytes)) {
+ read_index_ = end_index_;
return NULL;
- const char* current_read_ptr = read_ptr_;
- read_ptr_ += AlignInt(num_bytes, sizeof(uint32));
+ }
+ const char* current_read_ptr = payload_ + read_index_;
+ Advance(num_bytes);
return current_read_ptr;
}
-inline const char* PickleIterator::GetReadPointerAndAdvance(int num_elements,
- size_t size_element) {
+inline const char* PickleIterator::GetReadPointerAndAdvance(
+ int num_elements,
+ size_t size_element) {
// Check for int32 overflow.
int64 num_bytes = static_cast<int64>(num_elements) * size_element;
int num_bytes32 = static_cast<int>(num_bytes);
@@ -332,6 +345,6 @@ inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
char* write = mutable_payload() + write_offset_;
memcpy(write, data, length);
memset(write + length, 0, data_len - length);
- header_->payload_size = static_cast<uint32>(write_offset_ + length);
+ header_->payload_size = static_cast<uint32>(new_size);
write_offset_ = new_size;
}
diff --git a/chromium/base/pickle.h b/chromium/base/pickle.h
index dd34f54176c..2e3cd3664f8 100644
--- a/chromium/base/pickle.h
+++ b/chromium/base/pickle.h
@@ -20,13 +20,14 @@ class Pickle;
// while the PickleIterator object is in use.
class BASE_EXPORT PickleIterator {
public:
- PickleIterator() : read_ptr_(NULL), read_end_ptr_(NULL) {}
+ PickleIterator() : payload_(NULL), read_index_(0), end_index_(0) {}
explicit PickleIterator(const Pickle& pickle);
// Methods for reading the payload of the Pickle. To read from the start of
// the Pickle, create a PickleIterator from a Pickle. If successful, these
// methods return true. Otherwise, false is returned to indicate that the
- // result could not be extracted.
+ // result could not be extracted. It is not possible to read from iterator
+ // after that.
bool ReadBool(bool* result) WARN_UNUSED_RESULT;
bool ReadInt(int* result) WARN_UNUSED_RESULT;
bool ReadLong(long* result) WARN_UNUSED_RESULT;
@@ -61,11 +62,15 @@ class BASE_EXPORT PickleIterator {
// Read Type from Pickle.
template <typename Type>
- inline bool ReadBuiltinType(Type* result);
+ bool ReadBuiltinType(Type* result);
+
+ // Advance read_index_ but do not allow it to exceed end_index_.
+ // Keeps read_index_ aligned.
+ void Advance(size_t size);
// Get read pointer for Type and advance read pointer.
template<typename Type>
- inline const char* GetReadPointerAndAdvance();
+ const char* GetReadPointerAndAdvance();
// Get read pointer for |num_bytes| and advance read pointer. This method
// checks num_bytes for negativity and wrapping.
@@ -73,12 +78,12 @@ class BASE_EXPORT PickleIterator {
// Get read pointer for (num_elements * size_element) bytes and advance read
// pointer. This method checks for int overflow, negativity and wrapping.
- inline const char* GetReadPointerAndAdvance(int num_elements,
- size_t size_element);
+ const char* GetReadPointerAndAdvance(int num_elements,
+ size_t size_element);
- // Pointers to the Pickle data.
- const char* read_ptr_;
- const char* read_end_ptr_;
+ const char* payload_; // Start of our pickle's payload.
+ size_t read_index_; // Offset of the next readable byte in payload.
+ size_t end_index_; // Payload size.
FRIEND_TEST_ALL_PREFIXES(PickleTest, GetReadPointerAndAdvance);
};
@@ -277,7 +282,9 @@ class BASE_EXPORT Pickle {
}
// The payload is the pickle data immediately following the header.
- size_t payload_size() const { return header_->payload_size; }
+ size_t payload_size() const {
+ return header_ ? header_->payload_size : 0;
+ }
const char* payload() const {
return reinterpret_cast<const char*>(header_) + header_size_;
@@ -330,7 +337,7 @@ class BASE_EXPORT Pickle {
size_t write_offset_;
// Just like WriteBytes, but with a compile-time size, for performance.
- template<size_t length> void WriteBytesStatic(const void* data);
+ template<size_t length> void BASE_EXPORT WriteBytesStatic(const void* data);
// Writes a POD by copying its bytes.
template <typename T> bool WritePOD(const T& data) {
diff --git a/chromium/base/pickle_unittest.cc b/chromium/base/pickle_unittest.cc
index b1c5925f668..bec25d2f5e2 100644
--- a/chromium/base/pickle_unittest.cc
+++ b/chromium/base/pickle_unittest.cc
@@ -262,7 +262,7 @@ TEST(PickleTest, Resize) {
// one more byte should double the capacity
pickle.WriteData(data_ptr, 1);
- cur_payload += 5;
+ cur_payload += 8;
EXPECT_EQ(unit * 4, pickle.capacity_after_header());
EXPECT_EQ(cur_payload, pickle.payload_size());
}
diff --git a/chromium/base/platform_file.cc b/chromium/base/platform_file.cc
index bb411b893fe..70700fcf802 100644
--- a/chromium/base/platform_file.cc
+++ b/chromium/base/platform_file.cc
@@ -14,18 +14,4 @@ PlatformFileInfo::PlatformFileInfo()
PlatformFileInfo::~PlatformFileInfo() {}
-#if !defined(OS_NACL)
-PlatformFile CreatePlatformFile(const FilePath& name,
- int flags,
- bool* created,
- PlatformFileError* error) {
- if (name.ReferencesParent()) {
- if (error)
- *error = PLATFORM_FILE_ERROR_ACCESS_DENIED;
- return kInvalidPlatformFileValue;
- }
- return CreatePlatformFileUnsafe(name, flags, created, error);
-}
-#endif
-
} // namespace base
diff --git a/chromium/base/platform_file.h b/chromium/base/platform_file.h
index d9a82cbd3f4..27686d1044b 100644
--- a/chromium/base/platform_file.h
+++ b/chromium/base/platform_file.h
@@ -21,7 +21,7 @@ namespace base {
// ***************************************************************************
// ***** Don't use anything from this file anymore. It is being removed!
-// ***** Use base/files/base_file.h instead
+// ***** Use base/files/file.h instead
// ***************************************************************************
// PLATFORM_FILE_(OPEN|CREATE).* are mutually exclusive. You should specify
@@ -134,27 +134,6 @@ const PlatformFile kInvalidPlatformFileValue = -1;
BASE_EXPORT PlatformFileError ErrnoToPlatformFileError(int saved_errno);
#endif
-// Creates or opens the given file. If |created| is provided, it will be set to
-// true if a new file was created [or an old one truncated to zero length to
-// simulate a new file, which can happen with PLATFORM_FILE_CREATE_ALWAYS], and
-// false otherwise. |error| can be NULL.
-//
-// This function fails with 'access denied' if the |name| contains path
-// traversal ('..') components.
-BASE_EXPORT PlatformFile CreatePlatformFile(const FilePath& name,
- int flags,
- bool* created,
- PlatformFileError* error);
-
-// Same as CreatePlatformFile but allows paths with traversal (like \..\)
-// components. Use only with extreme care.
-BASE_EXPORT PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
- int flags,
- bool* created,
- PlatformFileError* error);
-
-BASE_EXPORT FILE* FdopenPlatformFile(PlatformFile file, const char* mode);
-
// Closes a file handle. Returns |true| on success and |false| otherwise.
BASE_EXPORT bool ClosePlatformFile(PlatformFile file);
diff --git a/chromium/base/platform_file_posix.cc b/chromium/base/platform_file_posix.cc
index 028a38276d6..cee1f611d11 100644
--- a/chromium/base/platform_file_posix.cc
+++ b/chromium/base/platform_file_posix.cc
@@ -117,108 +117,6 @@ static PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) {
} // namespace
-// NaCl doesn't implement system calls to open files directly.
-#if !defined(OS_NACL)
-// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here?
-PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
- int flags,
- bool* created,
- PlatformFileError* error) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- int open_flags = 0;
- if (flags & PLATFORM_FILE_CREATE)
- open_flags = O_CREAT | O_EXCL;
-
- if (created)
- *created = false;
-
- if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
- DCHECK(!open_flags);
- open_flags = O_CREAT | O_TRUNC;
- }
-
- if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
- DCHECK(!open_flags);
- DCHECK(flags & PLATFORM_FILE_WRITE);
- open_flags = O_TRUNC;
- }
-
- if (!open_flags && !(flags & PLATFORM_FILE_OPEN) &&
- !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
- NOTREACHED();
- errno = EOPNOTSUPP;
- if (error)
- *error = PLATFORM_FILE_ERROR_FAILED;
- return kInvalidPlatformFileValue;
- }
-
- if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) {
- open_flags |= O_RDWR;
- } else if (flags & PLATFORM_FILE_WRITE) {
- open_flags |= O_WRONLY;
- } else if (!(flags & PLATFORM_FILE_READ) &&
- !(flags & PLATFORM_FILE_WRITE_ATTRIBUTES) &&
- !(flags & PLATFORM_FILE_APPEND) &&
- !(flags & PLATFORM_FILE_OPEN_ALWAYS)) {
- NOTREACHED();
- }
-
- if (flags & PLATFORM_FILE_TERMINAL_DEVICE)
- open_flags |= O_NOCTTY | O_NDELAY;
-
- if (flags & PLATFORM_FILE_APPEND && flags & PLATFORM_FILE_READ)
- open_flags |= O_APPEND | O_RDWR;
- else if (flags & PLATFORM_FILE_APPEND)
- open_flags |= O_APPEND | O_WRONLY;
-
- COMPILE_ASSERT(O_RDONLY == 0, O_RDONLY_must_equal_zero);
-
- int mode = S_IRUSR | S_IWUSR;
-#if defined(OS_CHROMEOS)
- mode |= S_IRGRP | S_IROTH;
-#endif
-
- int descriptor =
- HANDLE_EINTR(open(name.value().c_str(), open_flags, mode));
-
- if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
- if (descriptor < 0) {
- open_flags |= O_CREAT;
- if (flags & PLATFORM_FILE_EXCLUSIVE_READ ||
- flags & PLATFORM_FILE_EXCLUSIVE_WRITE) {
- open_flags |= O_EXCL; // together with O_CREAT implies O_NOFOLLOW
- }
- descriptor = HANDLE_EINTR(
- open(name.value().c_str(), open_flags, mode));
- if (created && descriptor >= 0)
- *created = true;
- }
- }
-
- if (created && (descriptor >= 0) &&
- (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE)))
- *created = true;
-
- if ((descriptor >= 0) && (flags & PLATFORM_FILE_DELETE_ON_CLOSE)) {
- unlink(name.value().c_str());
- }
-
- if (error) {
- if (descriptor >= 0)
- *error = PLATFORM_FILE_OK;
- else
- *error = ErrnoToPlatformFileError(errno);
- }
-
- return descriptor;
-}
-
-FILE* FdopenPlatformFile(PlatformFile file, const char* mode) {
- return fdopen(file, mode);
-}
-#endif // !defined(OS_NACL)
-
bool ClosePlatformFile(PlatformFile file) {
base::ThreadRestrictions::AssertIOAllowed();
return !IGNORE_EINTR(close(file));
@@ -261,7 +159,7 @@ int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
int bytes_read = 0;
int rv;
do {
- rv = HANDLE_EINTR(read(file, data, size));
+ rv = HANDLE_EINTR(read(file, data + bytes_read, size - bytes_read));
if (rv <= 0)
break;
@@ -322,7 +220,7 @@ int WritePlatformFileAtCurrentPos(PlatformFile file,
int bytes_written = 0;
int rv;
do {
- rv = HANDLE_EINTR(write(file, data, size));
+ rv = HANDLE_EINTR(write(file, data + bytes_written, size - bytes_written));
if (rv <= 0)
break;
diff --git a/chromium/base/platform_file_unittest.cc b/chromium/base/platform_file_unittest.cc
index 9cf66a9369a..70058ea4ebf 100644
--- a/chromium/base/platform_file_unittest.cc
+++ b/chromium/base/platform_file_unittest.cc
@@ -25,6 +25,26 @@ int WriteFully(PlatformFile file, int64 offset,
return WritePlatformFile(file, offset, data, size);
}
+PlatformFile CreatePlatformFile(const FilePath& path,
+ int flags,
+ bool* created,
+ PlatformFileError* error) {
+ File file(path, flags);
+ if (!file.IsValid()) {
+ if (error)
+ *error = static_cast<PlatformFileError>(file.error_details());
+ return kInvalidPlatformFileValue;
+ }
+
+ if (created)
+ *created = file.created();
+
+ if (error)
+ *error = PLATFORM_FILE_OK;
+
+ return file.TakePlatformFile();
+}
+
} // namespace
TEST(PlatformFile, CreatePlatformFile) {
@@ -81,7 +101,7 @@ TEST(PlatformFile, CreatePlatformFile) {
error_code = PLATFORM_FILE_OK;
file = CreatePlatformFile(
file_path,
- PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_READ,
+ PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_WRITE,
&created,
&error_code);
EXPECT_NE(kInvalidPlatformFileValue, file);
diff --git a/chromium/base/platform_file_win.cc b/chromium/base/platform_file_win.cc
index b5e07d7665f..af786ab090a 100644
--- a/chromium/base/platform_file_win.cc
+++ b/chromium/base/platform_file_win.cc
@@ -12,119 +12,6 @@
#include "base/threading/thread_restrictions.h"
namespace base {
-PlatformFile CreatePlatformFileUnsafe(const FilePath& name,
- int flags,
- bool* created,
- PlatformFileError* error) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- DWORD disposition = 0;
- if (created)
- *created = false;
-
- if (flags & PLATFORM_FILE_OPEN)
- disposition = OPEN_EXISTING;
-
- if (flags & PLATFORM_FILE_CREATE) {
- DCHECK(!disposition);
- disposition = CREATE_NEW;
- }
-
- if (flags & PLATFORM_FILE_OPEN_ALWAYS) {
- DCHECK(!disposition);
- disposition = OPEN_ALWAYS;
- }
-
- if (flags & PLATFORM_FILE_CREATE_ALWAYS) {
- DCHECK(!disposition);
- disposition = CREATE_ALWAYS;
- }
-
- if (flags & PLATFORM_FILE_OPEN_TRUNCATED) {
- DCHECK(!disposition);
- DCHECK(flags & PLATFORM_FILE_WRITE);
- disposition = TRUNCATE_EXISTING;
- }
-
- if (!disposition) {
- NOTREACHED();
- return NULL;
- }
-
- DWORD access = 0;
- if (flags & PLATFORM_FILE_WRITE)
- access = GENERIC_WRITE;
- if (flags & PLATFORM_FILE_APPEND) {
- DCHECK(!access);
- access = FILE_APPEND_DATA;
- }
- if (flags & PLATFORM_FILE_READ)
- access |= GENERIC_READ;
- if (flags & PLATFORM_FILE_WRITE_ATTRIBUTES)
- access |= FILE_WRITE_ATTRIBUTES;
- if (flags & PLATFORM_FILE_EXECUTE)
- access |= GENERIC_EXECUTE;
-
- DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
- if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE))
- sharing |= FILE_SHARE_WRITE;
- if (flags & PLATFORM_FILE_SHARE_DELETE)
- sharing |= FILE_SHARE_DELETE;
-
- DWORD create_flags = 0;
- if (flags & PLATFORM_FILE_ASYNC)
- create_flags |= FILE_FLAG_OVERLAPPED;
- if (flags & PLATFORM_FILE_TEMPORARY)
- create_flags |= FILE_ATTRIBUTE_TEMPORARY;
- if (flags & PLATFORM_FILE_HIDDEN)
- create_flags |= FILE_ATTRIBUTE_HIDDEN;
- if (flags & PLATFORM_FILE_DELETE_ON_CLOSE)
- create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
- if (flags & PLATFORM_FILE_BACKUP_SEMANTICS)
- create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
-
- HANDLE file = CreateFile(name.value().c_str(), access, sharing, NULL,
- disposition, create_flags, NULL);
-
- if (INVALID_HANDLE_VALUE != file){
- // Don't allow directories to be opened without the proper flag (block ADS).
- if (!(flags & PLATFORM_FILE_BACKUP_SEMANTICS)) {
- BY_HANDLE_FILE_INFORMATION info = { 0 };
- BOOL result = GetFileInformationByHandle(file, &info);
- DCHECK(result);
- if (info.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
- FILE_ATTRIBUTE_REPARSE_POINT)) {
- CloseHandle(file);
- file = INVALID_HANDLE_VALUE;
- }
- }
- }
-
- if (created && (INVALID_HANDLE_VALUE != file)) {
- if (flags & (PLATFORM_FILE_OPEN_ALWAYS))
- *created = (ERROR_ALREADY_EXISTS != GetLastError());
- else if (flags & (PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_CREATE))
- *created = true;
- }
-
- if (error) {
- if (file != kInvalidPlatformFileValue)
- *error = PLATFORM_FILE_OK;
- else
- *error = LastErrorToPlatformFileError(GetLastError());
- }
-
- return file;
-}
-
-FILE* FdopenPlatformFile(PlatformFile file, const char* mode) {
- if (file == kInvalidPlatformFileValue)
- return NULL;
- int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file), 0);
- if (fd < 0)
- return NULL;
- return _fdopen(fd, mode);
-}
bool ClosePlatformFile(PlatformFile file) {
base::ThreadRestrictions::AssertIOAllowed();
diff --git a/chromium/base/port.h b/chromium/base/port.h
index af4e450afe4..0a04d55f0f6 100644
--- a/chromium/base/port.h
+++ b/chromium/base/port.h
@@ -8,6 +8,10 @@
#include <stdarg.h>
#include "build/build_config.h"
+// DEPRECATED: Use ...LL and ...ULL suffixes.
+// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C|
+// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our
+// definitions of them must exactly match theirs).
#ifdef COMPILER_MSVC
#define GG_LONGLONG(x) x##I64
#define GG_ULONGLONG(x) x##UI64
@@ -16,20 +20,10 @@
#define GG_ULONGLONG(x) x##ULL
#endif
-// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h>
-// to get the INTn_C and UINTn_C macros for integer constants. It's difficult
-// to guarantee any specific ordering of header includes, so it's difficult to
-// guarantee that the INTn_C macros can be defined by including <stdint.h> at
-// any specific point. Provide GG_INTn_C macros instead.
-
-#define GG_INT8_C(x) (x)
-#define GG_INT16_C(x) (x)
-#define GG_INT32_C(x) (x)
+// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can
+// just use the regular (U)INTn_C macros from <stdint.h>.
+// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros.
#define GG_INT64_C(x) GG_LONGLONG(x)
-
-#define GG_UINT8_C(x) (x ## U)
-#define GG_UINT16_C(x) (x ## U)
-#define GG_UINT32_C(x) (x ## U)
#define GG_UINT64_C(x) GG_ULONGLONG(x)
// It's possible for functions that use a va_list, such as StringPrintf, to
diff --git a/chromium/base/posix/file_descriptor_shuffle.cc b/chromium/base/posix/file_descriptor_shuffle.cc
index 7bc9e26eb58..d2fd39a95aa 100644
--- a/chromium/base/posix/file_descriptor_shuffle.cc
+++ b/chromium/base/posix/file_descriptor_shuffle.cc
@@ -19,20 +19,24 @@ bool PerformInjectiveMultimapDestructive(
int extra_fds[kMaxExtraFDs];
unsigned next_extra_fd = 0;
- // DANGER: this function may not allocate.
+ // DANGER: this function must not allocate or lock.
+ // Cannot use STL iterators here, since debug iterators use locks.
- for (InjectiveMultimap::iterator i = m->begin(); i != m->end(); ++i) {
+ for (size_t i_index = 0; i_index < m->size(); ++i_index) {
+ InjectiveMultimap::value_type* i = &(*m)[i_index];
int temp_fd = -1;
// We DCHECK the injectiveness of the mapping.
- for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
DCHECK(i->dest != j->dest) << "Both fd " << i->source
<< " and " << j->source << " map to " << i->dest;
}
const bool is_identity = i->source == i->dest;
- for (InjectiveMultimap::iterator j = i + 1; j != m->end(); ++j) {
+ for (size_t j_index = i_index + 1; j_index < m->size(); ++j_index) {
+ InjectiveMultimap::value_type* j = &(*m)[j_index];
if (!is_identity && i->dest == j->source) {
if (temp_fd == -1) {
if (!delegate->Duplicate(&temp_fd, i->dest))
diff --git a/chromium/base/posix/file_descriptor_shuffle.h b/chromium/base/posix/file_descriptor_shuffle.h
index 9cd918f2e15..5fa590bf47b 100644
--- a/chromium/base/posix/file_descriptor_shuffle.h
+++ b/chromium/base/posix/file_descriptor_shuffle.h
@@ -5,10 +5,10 @@
#ifndef BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
#define BASE_POSIX_FILE_DESCRIPTOR_SHUFFLE_H_
-// This code exists to perform the shuffling of file descriptors which is
-// commonly needed when forking subprocesses. The naive approve is very simple,
-// just call dup2 to setup the desired descriptors, but wrong. It's tough to
-// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly.
+// This code exists to shuffle file descriptors, which is commonly needed when
+// forking subprocesses. The naive approach (just call dup2 to set up the
+// desired descriptors) is very simple, but wrong: it won't handle edge cases
+// (like mapping 0 -> 1, 1 -> 0) correctly.
//
// In order to unittest this code, it's broken into the abstract action (an
// injective multimap) and the concrete code for dealing with file descriptors.
diff --git a/chromium/base/posix/global_descriptors.h b/chromium/base/posix/global_descriptors.h
index c7b9f87fa4b..3d7369c3174 100644
--- a/chromium/base/posix/global_descriptors.h
+++ b/chromium/base/posix/global_descriptors.h
@@ -41,7 +41,11 @@ class BASE_EXPORT GlobalDescriptors {
// Often we want a canonical descriptor for a given Key. In this case, we add
// the following constant to the key value:
+#if !defined(OS_ANDROID)
static const int kBaseDescriptor = 3; // 0, 1, 2 are already taken.
+#else
+ static const int kBaseDescriptor = 4; // 3 used by __android_log_write().
+#endif
// Return the singleton instance of GlobalDescriptors.
static GlobalDescriptors* GetInstance();
diff --git a/chromium/base/posix/unix_domain_socket_linux.cc b/chromium/base/posix/unix_domain_socket_linux.cc
index 757db983cd0..51b936ba45a 100644
--- a/chromium/base/posix/unix_domain_socket_linux.cc
+++ b/chromium/base/posix/unix_domain_socket_linux.cc
@@ -9,13 +9,35 @@
#include <sys/uio.h>
#include <unistd.h>
+#include <vector>
+
+#include "base/files/scoped_file.h"
#include "base/logging.h"
+#include "base/memory/scoped_vector.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/stl_util.h"
const size_t UnixDomainSocket::kMaxFileDescriptors = 16;
+// Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes
+// ownership of the newly allocated file descriptors to |one| and |two|.
+// Returns true on success.
+static bool CreateSocketPair(base::ScopedFD* one, base::ScopedFD* two) {
+ int raw_socks[2];
+ if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_socks) == -1)
+ return false;
+ one->reset(raw_socks[0]);
+ two->reset(raw_socks[1]);
+ return true;
+}
+
+// static
+bool UnixDomainSocket::EnableReceiveProcessId(int fd) {
+ const int enable = 1;
+ return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0;
+}
+
// static
bool UnixDomainSocket::SendMsg(int fd,
const void* buf,
@@ -57,8 +79,17 @@ bool UnixDomainSocket::SendMsg(int fd,
ssize_t UnixDomainSocket::RecvMsg(int fd,
void* buf,
size_t length,
- std::vector<int>* fds) {
- return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds);
+ ScopedVector<base::ScopedFD>* fds) {
+ return UnixDomainSocket::RecvMsgWithPid(fd, buf, length, fds, NULL);
+}
+
+// static
+ssize_t UnixDomainSocket::RecvMsgWithPid(int fd,
+ void* buf,
+ size_t length,
+ ScopedVector<base::ScopedFD>* fds,
+ base::ProcessId* pid) {
+ return UnixDomainSocket::RecvMsgWithFlags(fd, buf, length, 0, fds, pid);
}
// static
@@ -66,7 +97,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
void* buf,
size_t length,
int flags,
- std::vector<int>* fds) {
+ ScopedVector<base::ScopedFD>* fds,
+ base::ProcessId* out_pid) {
fds->clear();
struct msghdr msg = {};
@@ -74,7 +106,8 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
- char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors)];
+ char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) +
+ CMSG_SPACE(sizeof(struct ucred))];
msg.msg_control = control_buffer;
msg.msg_controllen = sizeof(control_buffer);
@@ -84,17 +117,24 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
int* wire_fds = NULL;
unsigned wire_fds_len = 0;
+ base::ProcessId pid = -1;
if (msg.msg_controllen > 0) {
struct cmsghdr* cmsg;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
- const unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
DCHECK(payload_len % sizeof(int) == 0);
+ DCHECK(wire_fds == NULL);
wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg));
wire_fds_len = payload_len / sizeof(int);
- break;
+ }
+ if (cmsg->cmsg_level == SOL_SOCKET &&
+ cmsg->cmsg_type == SCM_CREDENTIALS) {
+ DCHECK(payload_len == sizeof(struct ucred));
+ DCHECK(pid == -1);
+ pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid;
}
}
}
@@ -106,8 +146,21 @@ ssize_t UnixDomainSocket::RecvMsgWithFlags(int fd,
return -1;
}
- fds->resize(wire_fds_len);
- memcpy(vector_as_array(fds), wire_fds, sizeof(int) * wire_fds_len);
+ if (wire_fds) {
+ for (unsigned i = 0; i < wire_fds_len; ++i)
+ fds->push_back(new base::ScopedFD(wire_fds[i]));
+ }
+
+ if (out_pid) {
+ // |pid| will legitimately be -1 if we read EOF, so only DCHECK if we
+ // actually received a message. Unfortunately, Linux allows sending zero
+ // length messages, which are indistinguishable from EOF, so this check
+ // has false negatives.
+ if (r > 0 || msg.msg_controllen > 0)
+ DCHECK_GE(pid, 0);
+
+ *out_pid = pid;
+ }
return r;
}
@@ -130,44 +183,42 @@ ssize_t UnixDomainSocket::SendRecvMsgWithFlags(int fd,
int recvmsg_flags,
int* result_fd,
const Pickle& request) {
- int fds[2];
-
// This socketpair is only used for the IPC and is cleaned up before
// returning.
- if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds) == -1)
+ base::ScopedFD recv_sock, send_sock;
+ if (!CreateSocketPair(&recv_sock, &send_sock))
return -1;
- std::vector<int> fd_vector;
- fd_vector.push_back(fds[1]);
- if (!SendMsg(fd, request.data(), request.size(), fd_vector)) {
- close(fds[0]);
- close(fds[1]);
- return -1;
+ {
+ std::vector<int> send_fds;
+ send_fds.push_back(send_sock.get());
+ if (!SendMsg(fd, request.data(), request.size(), send_fds))
+ return -1;
}
- close(fds[1]);
- fd_vector.clear();
+ // Close the sending end of the socket right away so that if our peer closes
+ // it before sending a response (e.g., from exiting), RecvMsgWithFlags() will
+ // return EOF instead of hanging.
+ send_sock.reset();
+
+ ScopedVector<base::ScopedFD> recv_fds;
// When porting to OSX keep in mind it doesn't support MSG_NOSIGNAL, so the
// sender might get a SIGPIPE.
- const ssize_t reply_len = RecvMsgWithFlags(fds[0], reply, max_reply_len,
- recvmsg_flags, &fd_vector);
- close(fds[0]);
+ const ssize_t reply_len = RecvMsgWithFlags(
+ recv_sock.get(), reply, max_reply_len, recvmsg_flags, &recv_fds, NULL);
+ recv_sock.reset();
if (reply_len == -1)
return -1;
- if ((!fd_vector.empty() && result_fd == NULL) || fd_vector.size() > 1) {
- for (std::vector<int>::const_iterator
- i = fd_vector.begin(); i != fd_vector.end(); ++i) {
- close(*i);
- }
-
+ // If we received more file descriptors than caller expected, then we treat
+ // that as an error.
+ if (recv_fds.size() > (result_fd != NULL ? 1 : 0)) {
NOTREACHED();
-
return -1;
}
if (result_fd)
- *result_fd = fd_vector.empty() ? -1 : fd_vector[0];
+ *result_fd = recv_fds.empty() ? -1 : recv_fds[0]->release();
return reply_len;
}
diff --git a/chromium/base/posix/unix_domain_socket_linux.h b/chromium/base/posix/unix_domain_socket_linux.h
index 66fb8bb1bf6..59bb8840b9d 100644
--- a/chromium/base/posix/unix_domain_socket_linux.h
+++ b/chromium/base/posix/unix_domain_socket_linux.h
@@ -10,6 +10,9 @@
#include <vector>
#include "base/base_export.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_vector.h"
+#include "base/process/process_handle.h"
class Pickle;
@@ -18,6 +21,11 @@ class BASE_EXPORT UnixDomainSocket {
// Maximum number of file descriptors that can be read by RecvMsg().
static const size_t kMaxFileDescriptors;
+ // Use to enable receiving process IDs in RecvMsgWithPid. Should be called on
+ // the receiving socket (i.e., the socket passed to RecvMsgWithPid). Returns
+ // true if successful.
+ static bool EnableReceiveProcessId(int fd);
+
// Use sendmsg to write the given msg and include a vector of file
// descriptors. Returns true if successful.
static bool SendMsg(int fd,
@@ -30,7 +38,17 @@ class BASE_EXPORT UnixDomainSocket {
static ssize_t RecvMsg(int fd,
void* msg,
size_t length,
- std::vector<int>* fds);
+ ScopedVector<base::ScopedFD>* fds);
+
+ // Same as RecvMsg above, but also returns the sender's process ID (as seen
+ // from the caller's namespace). However, before using this function to
+ // receive process IDs, EnableReceiveProcessId() should be called on the
+ // receiving socket.
+ static ssize_t RecvMsgWithPid(int fd,
+ void* msg,
+ size_t length,
+ ScopedVector<base::ScopedFD>* fds,
+ base::ProcessId* pid);
// Perform a sendmsg/recvmsg pair.
// 1. This process creates a UNIX SEQPACKET socketpair. Using
@@ -70,7 +88,8 @@ class BASE_EXPORT UnixDomainSocket {
void* msg,
size_t length,
int flags,
- std::vector<int>* fds);
+ ScopedVector<base::ScopedFD>* fds,
+ base::ProcessId* pid);
};
#endif // BASE_POSIX_UNIX_DOMAIN_SOCKET_LINUX_H_
diff --git a/chromium/base/posix/unix_domain_socket_linux_unittest.cc b/chromium/base/posix/unix_domain_socket_linux_unittest.cc
index 22bb172ad5b..7b2e2af2332 100644
--- a/chromium/base/posix/unix_domain_socket_linux_unittest.cc
+++ b/chromium/base/posix/unix_domain_socket_linux_unittest.cc
@@ -9,6 +9,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
+#include "base/memory/scoped_vector.h"
#include "base/pickle.h"
#include "base/posix/unix_domain_socket_linux.h"
#include "base/synchronization/waitable_event.h"
@@ -25,8 +27,8 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
int fds[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
- file_util::ScopedFD scoped_fd0(&fds[0]);
- file_util::ScopedFD scoped_fd1(&fds[1]);
+ ScopedFD scoped_fd0(fds[0]);
+ ScopedFD scoped_fd1(fds[1]);
// Have the thread send a synchronous message via the socket.
Pickle request;
@@ -37,7 +39,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
request));
// Receive the message.
- std::vector<int> message_fds;
+ ScopedVector<base::ScopedFD> message_fds;
uint8_t buffer[16];
ASSERT_EQ(static_cast<int>(request.size()),
UnixDomainSocket::RecvMsg(fds[0], buffer, sizeof(buffer),
@@ -45,7 +47,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
ASSERT_EQ(1U, message_fds.size());
// Close the reply FD.
- ASSERT_EQ(0, IGNORE_EINTR(close(message_fds.front())));
+ message_fds.clear();
// Check that the thread didn't get blocked.
WaitableEvent event(false, false);
@@ -62,7 +64,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) {
ASSERT_EQ(0, sigaction(SIGPIPE, &act, &oldact));
int fds[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
- file_util::ScopedFD scoped_fd1(&fds[1]);
+ ScopedFD scoped_fd1(fds[1]);
ASSERT_EQ(0, IGNORE_EINTR(close(fds[0])));
// Have the thread send a synchronous message via the socket. Unless the
@@ -76,6 +78,84 @@ TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) {
ASSERT_EQ(0, sigaction(SIGPIPE, &oldact, NULL));
}
+// Simple sanity check within a single process that receiving PIDs works.
+TEST(UnixDomainSocketTest, RecvPid) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ static const char kHello[] = "hello";
+ ASSERT_TRUE(UnixDomainSocket::SendMsg(
+ send_sock.get(), kHello, sizeof(kHello), std::vector<int>()));
+
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ base::ProcessId sender_pid;
+ ScopedVector<base::ScopedFD> fd_vec;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), buf, sizeof(buf), &fd_vec, &sender_pid);
+ ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
+ ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ ASSERT_EQ(0U, fd_vec.size());
+
+ ASSERT_EQ(getpid(), sender_pid);
+}
+
+// Same as above, but send the max number of file descriptors too.
+TEST(UnixDomainSocketTest, RecvPidWithMaxDescriptors) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ static const char kHello[] = "hello";
+ std::vector<int> send_fds(UnixDomainSocket::kMaxFileDescriptors,
+ send_sock.get());
+ ASSERT_TRUE(UnixDomainSocket::SendMsg(
+ send_sock.get(), kHello, sizeof(kHello), send_fds));
+
+ // Extra receiving buffer space to make sure we really received only
+ // sizeof(kHello) bytes and it wasn't just truncated to fit the buffer.
+ char buf[sizeof(kHello) + 1];
+ base::ProcessId sender_pid;
+ ScopedVector<base::ScopedFD> recv_fds;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), buf, sizeof(buf), &recv_fds, &sender_pid);
+ ASSERT_EQ(sizeof(kHello), static_cast<size_t>(nread));
+ ASSERT_EQ(0, memcmp(buf, kHello, sizeof(kHello)));
+ ASSERT_EQ(UnixDomainSocket::kMaxFileDescriptors, recv_fds.size());
+
+ ASSERT_EQ(getpid(), sender_pid);
+}
+
+// Check that RecvMsgWithPid doesn't DCHECK fail when reading EOF from a
+// disconnected socket.
+TEST(UnixDomianSocketTest, RecvPidDisconnectedSocket) {
+ int fds[2];
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
+ base::ScopedFD recv_sock(fds[0]);
+ base::ScopedFD send_sock(fds[1]);
+
+ ASSERT_TRUE(UnixDomainSocket::EnableReceiveProcessId(recv_sock.get()));
+
+ send_sock.reset();
+
+ char ch;
+ base::ProcessId sender_pid;
+ ScopedVector<base::ScopedFD> recv_fds;
+ const ssize_t nread = UnixDomainSocket::RecvMsgWithPid(
+ recv_sock.get(), &ch, sizeof(ch), &recv_fds, &sender_pid);
+ ASSERT_EQ(0, nread);
+ ASSERT_EQ(-1, sender_pid);
+ ASSERT_EQ(0U, recv_fds.size());
+}
+
} // namespace
} // namespace base
diff --git a/chromium/base/power_monitor/power_monitor_device_source_win.cc b/chromium/base/power_monitor/power_monitor_device_source_win.cc
index 6609bd0e9e0..b8b16e1d344 100644
--- a/chromium/base/power_monitor/power_monitor_device_source_win.cc
+++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc
@@ -55,7 +55,7 @@ void ProcessWmPowerBroadcastMessage(WPARAM event_id) {
bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
SYSTEM_POWER_STATUS status;
if (!GetSystemPowerStatus(&status)) {
- DLOG_GETLASTERROR(ERROR) << "GetSystemPowerStatus failed";
+ DPLOG(ERROR) << "GetSystemPowerStatus failed";
return false;
}
return (status.ACLineStatus == 0);
@@ -63,7 +63,7 @@ bool PowerMonitorDeviceSource::IsOnBatteryPowerImpl() {
PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow()
: instance_(NULL), message_hwnd_(NULL) {
- if (MessageLoop::current()->type() != MessageLoop::TYPE_UI) {
+ if (!MessageLoopForUI::IsCurrent()) {
// Creating this window in (e.g.) a renderer inhibits shutdown on Windows.
// See http://crbug.com/230122. TODO(vandebo): http://crbug.com/236031
DLOG(ERROR)
diff --git a/chromium/base/prefs/OWNERS b/chromium/base/prefs/OWNERS
index 024da1cb54c..97ab6952245 100644
--- a/chromium/base/prefs/OWNERS
+++ b/chromium/base/prefs/OWNERS
@@ -1,4 +1,5 @@
battre@chromium.org
bauerb@chromium.org
+gab@chromium.org
mnissler@chromium.org
pam@chromium.org
diff --git a/chromium/base/prefs/json_pref_store.cc b/chromium/base/prefs/json_pref_store.cc
index ad97b8459cb..9180984d0e8 100644
--- a/chromium/base/prefs/json_pref_store.cc
+++ b/chromium/base/prefs/json_pref_store.cc
@@ -13,6 +13,7 @@
#include "base/json/json_string_value_serializer.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop_proxy.h"
+#include "base/prefs/pref_filter.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
@@ -36,19 +37,23 @@ class FileThreadDeserializer
origin_loop_proxy_(base::MessageLoopProxy::current()) {
}
- void Start(const base::FilePath& path) {
+ void Start(const base::FilePath& path,
+ const base::FilePath& alternate_path) {
DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
+ // TODO(gab): This should use PostTaskAndReplyWithResult instead of using
+ // the |error_| member to pass data across tasks.
sequenced_task_runner_->PostTask(
FROM_HERE,
base::Bind(&FileThreadDeserializer::ReadFileAndReport,
- this, path));
+ this, path, alternate_path));
}
// Deserializes JSON on the sequenced task runner.
- void ReadFileAndReport(const base::FilePath& path) {
+ void ReadFileAndReport(const base::FilePath& path,
+ const base::FilePath& alternate_path) {
DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
- value_.reset(DoReading(path, &error_, &no_dir_));
+ value_.reset(DoReading(path, alternate_path, &error_, &no_dir_));
origin_loop_proxy_->PostTask(
FROM_HERE,
@@ -58,12 +63,18 @@ class FileThreadDeserializer
// Reports deserialization result on the origin thread.
void ReportOnOriginThread() {
DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
- delegate_->OnFileRead(value_.release(), error_, no_dir_);
+ delegate_->OnFileRead(value_.Pass(), error_, no_dir_);
}
static base::Value* DoReading(const base::FilePath& path,
+ const base::FilePath& alternate_path,
PersistentPrefStore::PrefReadError* error,
bool* no_dir) {
+ if (!base::PathExists(path) && !alternate_path.empty() &&
+ base::PathExists(alternate_path)) {
+ base::Move(alternate_path, path);
+ }
+
int error_code;
std::string error_msg;
JSONFileValueSerializer serializer(path);
@@ -151,14 +162,34 @@ scoped_refptr<base::SequencedTaskRunner> JsonPrefStore::GetTaskRunnerForFile(
}
JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
- base::SequencedTaskRunner* sequenced_task_runner)
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
: path_(filename),
sequenced_task_runner_(sequenced_task_runner),
prefs_(new base::DictionaryValue()),
read_only_(false),
writer_(filename, sequenced_task_runner),
+ pref_filter_(pref_filter.Pass()),
initialized_(false),
- read_error_(PREF_READ_ERROR_OTHER) {}
+ filtering_in_progress_(false),
+ read_error_(PREF_READ_ERROR_NONE) {
+}
+
+JsonPrefStore::JsonPrefStore(const base::FilePath& filename,
+ const base::FilePath& alternate_filename,
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter)
+ : path_(filename),
+ alternate_path_(alternate_filename),
+ sequenced_task_runner_(sequenced_task_runner),
+ prefs_(new base::DictionaryValue()),
+ read_only_(false),
+ writer_(filename, sequenced_task_runner),
+ pref_filter_(pref_filter.Pass()),
+ initialized_(false),
+ filtering_in_progress_(false),
+ read_error_(PREF_READ_ERROR_NONE) {
+}
bool JsonPrefStore::GetValue(const std::string& key,
const base::Value** result) const {
@@ -221,6 +252,12 @@ void JsonPrefStore::RemoveValue(const std::string& key) {
ReportValueChanged(key);
}
+void JsonPrefStore::RemoveValueSilently(const std::string& key) {
+ prefs_->RemovePath(key, NULL);
+ if (!read_only_)
+ writer_.ScheduleWrite(this);
+}
+
bool JsonPrefStore::ReadOnly() const {
return read_only_;
}
@@ -231,23 +268,27 @@ PersistentPrefStore::PrefReadError JsonPrefStore::GetReadError() const {
PersistentPrefStore::PrefReadError JsonPrefStore::ReadPrefs() {
if (path_.empty()) {
- OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
+ OnFileRead(
+ scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
}
PrefReadError error;
bool no_dir;
- base::Value* value =
- FileThreadDeserializer::DoReading(path_, &error, &no_dir);
- OnFileRead(value, error, no_dir);
- return error;
+ scoped_ptr<base::Value> value(
+ FileThreadDeserializer::DoReading(path_, alternate_path_, &error,
+ &no_dir));
+ OnFileRead(value.Pass(), error, no_dir);
+ return filtering_in_progress_ ? PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE :
+ error;
}
-void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) {
+void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
initialized_ = false;
error_delegate_.reset(error_delegate);
if (path_.empty()) {
- OnFileRead(NULL, PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
+ OnFileRead(
+ scoped_ptr<base::Value>(), PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
return;
}
@@ -255,7 +296,7 @@ void JsonPrefStore::ReadPrefsAsync(ReadErrorDelegate *error_delegate) {
// in the end.
scoped_refptr<FileThreadDeserializer> deserializer(
new FileThreadDeserializer(this, sequenced_task_runner_.get()));
- deserializer->Start(path_);
+ deserializer->Start(path_, alternate_path_);
}
void JsonPrefStore::CommitPendingWrite() {
@@ -264,63 +305,117 @@ void JsonPrefStore::CommitPendingWrite() {
}
void JsonPrefStore::ReportValueChanged(const std::string& key) {
+ if (pref_filter_)
+ pref_filter_->FilterUpdate(key);
+
FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
+
if (!read_only_)
writer_.ScheduleWrite(this);
}
-void JsonPrefStore::OnFileRead(base::Value* value_owned,
+void JsonPrefStore::RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write) {
+ writer_.RegisterOnNextSuccessfulWriteCallback(on_next_successful_write);
+}
+
+void JsonPrefStore::OnFileRead(scoped_ptr<base::Value> value,
PersistentPrefStore::PrefReadError error,
bool no_dir) {
- scoped_ptr<base::Value> value(value_owned);
+ scoped_ptr<base::DictionaryValue> unfiltered_prefs(new base::DictionaryValue);
+
read_error_ = error;
- if (no_dir) {
+ bool initialization_successful = !no_dir;
+
+ if (initialization_successful) {
+ switch (read_error_) {
+ case PREF_READ_ERROR_ACCESS_DENIED:
+ case PREF_READ_ERROR_FILE_OTHER:
+ case PREF_READ_ERROR_FILE_LOCKED:
+ case PREF_READ_ERROR_JSON_TYPE:
+ case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
+ read_only_ = true;
+ break;
+ case PREF_READ_ERROR_NONE:
+ DCHECK(value.get());
+ unfiltered_prefs.reset(
+ static_cast<base::DictionaryValue*>(value.release()));
+ break;
+ case PREF_READ_ERROR_NO_FILE:
+ // If the file just doesn't exist, maybe this is first run. In any case
+ // there's no harm in writing out default prefs in this case.
+ break;
+ case PREF_READ_ERROR_JSON_PARSE:
+ case PREF_READ_ERROR_JSON_REPEAT:
+ break;
+ case PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE:
+ // This is a special error code to be returned by ReadPrefs when it
+ // can't complete synchronously, it should never be returned by the read
+ // operation itself.
+ NOTREACHED();
+ break;
+ case PREF_READ_ERROR_LEVELDB_IO:
+ case PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY:
+ case PREF_READ_ERROR_LEVELDB_CORRUPTION:
+ // These are specific to LevelDBPrefStore.
+ NOTREACHED();
+ case PREF_READ_ERROR_MAX_ENUM:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ if (pref_filter_) {
+ filtering_in_progress_ = true;
+ const PrefFilter::PostFilterOnLoadCallback post_filter_on_load_callback(
+ base::Bind(
+ &JsonPrefStore::FinalizeFileRead, this, initialization_successful));
+ pref_filter_->FilterOnLoad(post_filter_on_load_callback,
+ unfiltered_prefs.Pass());
+ } else {
+ FinalizeFileRead(initialization_successful, unfiltered_prefs.Pass(), false);
+ }
+}
+
+JsonPrefStore::~JsonPrefStore() {
+ CommitPendingWrite();
+}
+
+bool JsonPrefStore::SerializeData(std::string* output) {
+ if (pref_filter_)
+ pref_filter_->FilterSerializeData(prefs_.get());
+
+ JSONStringValueSerializer serializer(output);
+ serializer.set_pretty_print(true);
+ return serializer.Serialize(*prefs_);
+}
+
+void JsonPrefStore::FinalizeFileRead(bool initialization_successful,
+ scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write) {
+ filtering_in_progress_ = false;
+
+ if (!initialization_successful) {
FOR_EACH_OBSERVER(PrefStore::Observer,
observers_,
OnInitializationCompleted(false));
return;
}
+ prefs_ = prefs.Pass();
+
initialized_ = true;
- switch (error) {
- case PREF_READ_ERROR_ACCESS_DENIED:
- case PREF_READ_ERROR_FILE_OTHER:
- case PREF_READ_ERROR_FILE_LOCKED:
- case PREF_READ_ERROR_JSON_TYPE:
- case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
- read_only_ = true;
- break;
- case PREF_READ_ERROR_NONE:
- DCHECK(value.get());
- prefs_.reset(static_cast<base::DictionaryValue*>(value.release()));
- break;
- case PREF_READ_ERROR_NO_FILE:
- // If the file just doesn't exist, maybe this is first run. In any case
- // there's no harm in writing out default prefs in this case.
- break;
- case PREF_READ_ERROR_JSON_PARSE:
- case PREF_READ_ERROR_JSON_REPEAT:
- break;
- default:
- NOTREACHED() << "Unknown error: " << error;
- }
+ if (schedule_write && !read_only_)
+ writer_.ScheduleWrite(this);
- if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
- error_delegate_->OnError(error);
+ if (error_delegate_ && read_error_ != PREF_READ_ERROR_NONE)
+ error_delegate_->OnError(read_error_);
FOR_EACH_OBSERVER(PrefStore::Observer,
observers_,
OnInitializationCompleted(true));
-}
-
-JsonPrefStore::~JsonPrefStore() {
- CommitPendingWrite();
-}
-bool JsonPrefStore::SerializeData(std::string* output) {
- JSONStringValueSerializer serializer(output);
- serializer.set_pretty_print(true);
- return serializer.Serialize(*prefs_);
+ return;
}
diff --git a/chromium/base/prefs/json_pref_store.h b/chromium/base/prefs/json_pref_store.h
index 21fc8f95ac9..6ceea688548 100644
--- a/chromium/base/prefs/json_pref_store.h
+++ b/chromium/base/prefs/json_pref_store.h
@@ -9,15 +9,19 @@
#include <string>
#include "base/basictypes.h"
+#include "base/callback_forward.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/important_file_writer.h"
#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/observer_list.h"
#include "base/prefs/base_prefs_export.h"
#include "base/prefs/persistent_pref_store.h"
+class PrefFilter;
+
namespace base {
class DictionaryValue;
class FilePath;
@@ -30,7 +34,8 @@ class Value;
// A writable PrefStore implementation that is used for user preferences.
class BASE_PREFS_EXPORT JsonPrefStore
: public PersistentPrefStore,
- public base::ImportantFileWriter::DataSerializer {
+ public base::ImportantFileWriter::DataSerializer,
+ public base::SupportsWeakPtr<JsonPrefStore> {
public:
// Returns instance of SequencedTaskRunner which guarantees that file
// operations on the same file will be executed in sequenced order.
@@ -38,10 +43,22 @@ class BASE_PREFS_EXPORT JsonPrefStore
const base::FilePath& pref_filename,
base::SequencedWorkerPool* worker_pool);
- // |sequenced_task_runner| is must be a shutdown-blocking task runner, ideally
- // created by GetTaskRunnerForFile() method above.
+ // Same as the constructor below with no alternate filename.
+ JsonPrefStore(const base::FilePath& pref_filename,
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
+
+ // |sequenced_task_runner| must be a shutdown-blocking task runner, ideally
+ // created by the GetTaskRunnerForFile() method above.
+ // |pref_filename| is the path to the file to read prefs from.
+ // |pref_alternate_filename| is the path to an alternate file which the
+ // desired prefs may have previously been written to. If |pref_filename|
+ // doesn't exist and |pref_alternate_filename| does, |pref_alternate_filename|
+ // will be moved to |pref_filename| before the read occurs.
JsonPrefStore(const base::FilePath& pref_filename,
- base::SequencedTaskRunner* sequenced_task_runner);
+ const base::FilePath& pref_alternate_filename,
+ base::SequencedTaskRunner* sequenced_task_runner,
+ scoped_ptr<PrefFilter> pref_filter);
// PrefStore overrides:
virtual bool GetValue(const std::string& key,
@@ -60,16 +77,37 @@ class BASE_PREFS_EXPORT JsonPrefStore
virtual void RemoveValue(const std::string& key) OVERRIDE;
virtual bool ReadOnly() const OVERRIDE;
virtual PrefReadError GetReadError() const OVERRIDE;
+ // Note this method may be asynchronous if this instance has a |pref_filter_|
+ // in which case it will return PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE.
+ // See details in pref_filter.h.
virtual PrefReadError ReadPrefs() OVERRIDE;
virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE;
virtual void CommitPendingWrite() OVERRIDE;
virtual void ReportValueChanged(const std::string& key) OVERRIDE;
- // This method is called after JSON file has been read. Method takes
- // ownership of the |value| pointer. Note, this method is used with
- // asynchronous file reading, so class exposes it only for the internal needs.
- // (read: do not call it manually).
- void OnFileRead(base::Value* value_owned, PrefReadError error, bool no_dir);
+ // Just like RemoveValue(), but doesn't notify observers. Used when doing some
+ // cleanup that shouldn't otherwise alert observers.
+ void RemoveValueSilently(const std::string& key);
+
+ // Registers |on_next_successful_write| to be called once, on the next
+ // successful write event of |writer_|.
+ void RegisterOnNextSuccessfulWriteCallback(
+ const base::Closure& on_next_successful_write);
+
+ // This method is called after the JSON file has been read. It then hands
+ // |value| (or an empty dictionary in some read error cases) to the
+ // |pref_filter| if one is set. It also gives a callback pointing at
+ // FinalizeFileRead() to that |pref_filter_| which is then responsible for
+ // invoking it when done. If there is no |pref_filter_|, FinalizeFileRead()
+ // is invoked directly.
+ // Note, this method is used with asynchronous file reading, so this class
+ // exposes it only for the internal needs (read: do not call it manually).
+ // TODO(gab): Move this method to the private section and hand a callback to
+ // it to FileThreadDeserializer rather than exposing this public method and
+ // giving a JsonPrefStore* to FileThreadDeserializer.
+ void OnFileRead(scoped_ptr<base::Value> value,
+ PrefReadError error,
+ bool no_dir);
private:
virtual ~JsonPrefStore();
@@ -77,7 +115,19 @@ class BASE_PREFS_EXPORT JsonPrefStore
// ImportantFileWriter::DataSerializer overrides:
virtual bool SerializeData(std::string* output) OVERRIDE;
- base::FilePath path_;
+ // This method is called after the JSON file has been read and the result has
+ // potentially been intercepted and modified by |pref_filter_|.
+ // |initialization_successful| is pre-determined by OnFileRead() and should
+ // be used when reporting OnInitializationCompleted().
+ // |schedule_write| indicates whether a write should be immediately scheduled
+ // (typically because the |pref_filter_| has already altered the |prefs|) --
+ // this will be ignored if this store is read-only.
+ void FinalizeFileRead(bool initialization_successful,
+ scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write);
+
+ const base::FilePath path_;
+ const base::FilePath alternate_path_;
const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
scoped_ptr<base::DictionaryValue> prefs_;
@@ -87,11 +137,13 @@ class BASE_PREFS_EXPORT JsonPrefStore
// Helper for safely writing pref data.
base::ImportantFileWriter writer_;
+ scoped_ptr<PrefFilter> pref_filter_;
ObserverList<PrefStore::Observer, true> observers_;
scoped_ptr<ReadErrorDelegate> error_delegate_;
bool initialized_;
+ bool filtering_in_progress_;
PrefReadError read_error_;
std::set<std::string> keys_need_empty_value_;
diff --git a/chromium/base/prefs/json_pref_store_unittest.cc b/chromium/base/prefs/json_pref_store_unittest.cc
index a26afd71375..441c2293298 100644
--- a/chromium/base/prefs/json_pref_store_unittest.cc
+++ b/chromium/base/prefs/json_pref_store_unittest.cc
@@ -4,11 +4,14 @@
#include "base/prefs/json_pref_store.h"
+#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
+#include "base/prefs/pref_filter.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
@@ -24,6 +27,50 @@ namespace {
const char kHomePage[] = "homepage";
+// A PrefFilter that will intercept all calls to FilterOnLoad() and hold on
+// to the |prefs| until explicitly asked to release them.
+class InterceptingPrefFilter : public PrefFilter {
+ public:
+ InterceptingPrefFilter();
+ virtual ~InterceptingPrefFilter();
+
+ // PrefFilter implementation:
+ virtual void FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<base::DictionaryValue> pref_store_contents) OVERRIDE;
+ virtual void FilterUpdate(const std::string& path) OVERRIDE {}
+ virtual void FilterSerializeData(
+ base::DictionaryValue* pref_store_contents) OVERRIDE {}
+
+ bool has_intercepted_prefs() const { return intercepted_prefs_ != NULL; }
+
+ // Finalize an intercepted read, handing |intercepted_prefs_| back to its
+ // JsonPrefStore.
+ void ReleasePrefs();
+
+ private:
+ PostFilterOnLoadCallback post_filter_on_load_callback_;
+ scoped_ptr<base::DictionaryValue> intercepted_prefs_;
+
+ DISALLOW_COPY_AND_ASSIGN(InterceptingPrefFilter);
+};
+
+InterceptingPrefFilter::InterceptingPrefFilter() {}
+InterceptingPrefFilter::~InterceptingPrefFilter() {}
+
+void InterceptingPrefFilter::FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<base::DictionaryValue> pref_store_contents) {
+ post_filter_on_load_callback_ = post_filter_on_load_callback;
+ intercepted_prefs_ = pref_store_contents.Pass();
+}
+
+void InterceptingPrefFilter::ReleasePrefs() {
+ EXPECT_FALSE(post_filter_on_load_callback_.is_null());
+ post_filter_on_load_callback_.Run(intercepted_prefs_.Pass(), false);
+ post_filter_on_load_callback_.Reset();
+}
+
class MockPrefStoreObserver : public PrefStore::Observer {
public:
MOCK_METHOD1(OnPrefValueChanged, void (const std::string&));
@@ -47,6 +94,13 @@ class JsonPrefStoreTest : public testing::Test {
ASSERT_TRUE(PathExists(data_dir_));
}
+ virtual void TearDown() OVERRIDE {
+ // Make sure all pending tasks have been processed (e.g., deleting the
+ // JsonPrefStore may post write tasks).
+ message_loop_.PostTask(FROM_HERE, MessageLoop::QuitWhenIdleClosure());
+ message_loop_.Run();
+ }
+
// The path to temporary directory used to contain the test operations.
base::ScopedTempDir temp_dir_;
// The path to the directory where the test data is stored.
@@ -60,7 +114,26 @@ TEST_F(JsonPrefStoreTest, NonExistentFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- bogus_input_file, message_loop_.message_loop_proxy().get());
+ bogus_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
+ EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+}
+
+// Test fallback behavior for a nonexistent file and alternate file.
+TEST_F(JsonPrefStoreTest, NonExistentFileAndAlternateFile) {
+ base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
+ base::FilePath bogus_alternate_input_file =
+ data_dir_.AppendASCII("read_alternate.txt");
+ ASSERT_FALSE(PathExists(bogus_input_file));
+ ASSERT_FALSE(PathExists(bogus_alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ bogus_input_file,
+ bogus_alternate_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_NO_FILE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -72,7 +145,9 @@ TEST_F(JsonPrefStoreTest, InvalidFile) {
base::FilePath invalid_file = temp_dir_.path().AppendASCII("invalid.json");
ASSERT_TRUE(base::CopyFile(invalid_file_original, invalid_file));
scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(invalid_file, message_loop_.message_loop_proxy().get());
+ new JsonPrefStore(invalid_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
EXPECT_EQ(PersistentPrefStore::PREF_READ_ERROR_JSON_PARSE,
pref_store->ReadPrefs());
EXPECT_FALSE(pref_store->ReadOnly());
@@ -152,15 +227,18 @@ void RunBasicJsonPrefStoreTest(JsonPrefStore* pref_store,
TEST_F(JsonPrefStoreTest, Basic) {
ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
- temp_dir_.path().AppendASCII("write.json")));
+ temp_dir_.path().AppendASCII("write.json")));
// Test that the persistent value can be loaded.
base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(PathExists(input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
- ASSERT_FALSE(pref_store->ReadOnly());
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
// The JSON file looks like this:
// {
@@ -178,13 +256,15 @@ TEST_F(JsonPrefStoreTest, Basic) {
TEST_F(JsonPrefStoreTest, BasicAsync) {
ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
- temp_dir_.path().AppendASCII("write.json")));
+ temp_dir_.path().AppendASCII("write.json")));
// Test that the persistent value can be loaded.
base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
ASSERT_TRUE(PathExists(input_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(input_file, message_loop_.message_loop_proxy().get());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
{
MockPrefStoreObserver mock_observer;
@@ -199,7 +279,8 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
RunLoop().RunUntilIdle();
pref_store->RemoveObserver(&mock_observer);
- ASSERT_FALSE(pref_store->ReadOnly());
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
}
// The JSON file looks like this:
@@ -219,8 +300,10 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ pref_file,
+ message_loop_.message_loop_proxy(),
+ scoped_ptr<PrefFilter>());
// Set some keys with empty values.
pref_store->SetValue("list", new base::ListValue);
@@ -231,7 +314,10 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
MessageLoop::current()->RunUntilIdle();
// Reload.
- pref_store = new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
+ pref_store = new JsonPrefStore(
+ pref_file,
+ message_loop_.message_loop_proxy(),
+ scoped_ptr<PrefFilter>());
ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
ASSERT_FALSE(pref_store->ReadOnly());
@@ -243,12 +329,35 @@ TEST_F(JsonPrefStoreTest, PreserveEmptyValues) {
EXPECT_TRUE(DictionaryValue().Equals(result));
}
+// This test is just documenting some potentially non-obvious behavior. It
+// shouldn't be taken as normative.
+TEST_F(JsonPrefStoreTest, RemoveClearsEmptyParent) {
+ FilePath pref_file = temp_dir_.path().AppendASCII("empty_values.json");
+
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ pref_file,
+ message_loop_.message_loop_proxy(),
+ scoped_ptr<PrefFilter>());
+
+ base::DictionaryValue* dict = new base::DictionaryValue;
+ dict->SetString("key", "value");
+ pref_store->SetValue("dict", dict);
+
+ pref_store->RemoveValue("dict.key");
+
+ const base::Value* retrieved_dict = NULL;
+ bool has_dict = pref_store->GetValue("dict", &retrieved_dict);
+ EXPECT_FALSE(has_dict);
+}
+
// Tests asynchronous reading of the file when there is no file.
TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
base::FilePath bogus_input_file = data_dir_.AppendASCII("read.txt");
ASSERT_FALSE(PathExists(bogus_input_file));
scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
- bogus_input_file, message_loop_.message_loop_proxy().get());
+ bogus_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
MockPrefStoreObserver mock_observer;
pref_store->AddObserver(&mock_observer);
@@ -264,4 +373,302 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
EXPECT_FALSE(pref_store->ReadOnly());
}
+TEST_F(JsonPrefStoreTest, ReadWithInterceptor) {
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+
+ // Test that the persistent value can be loaded.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_TRUE(PathExists(input_file));
+
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+ new InterceptingPrefFilter());
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+ intercepting_pref_filter.get();
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file,
+ message_loop_.message_loop_proxy().get(),
+ intercepting_pref_filter.PassAs<PrefFilter>());
+
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE,
+ pref_store->ReadPrefs());
+ EXPECT_FALSE(pref_store->ReadOnly());
+
+ // The store shouldn't be considered initialized until the interceptor
+ // returns.
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_FALSE(pref_store->IsInitializationComplete());
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
+
+ raw_intercepting_pref_filter_->ReleasePrefs();
+
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, ReadAsyncWithInterceptor) {
+ ASSERT_TRUE(base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+
+ // Test that the persistent value can be loaded.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ ASSERT_TRUE(PathExists(input_file));
+
+ scoped_ptr<InterceptingPrefFilter> intercepting_pref_filter(
+ new InterceptingPrefFilter());
+ InterceptingPrefFilter* raw_intercepting_pref_filter_ =
+ intercepting_pref_filter.get();
+ scoped_refptr<JsonPrefStore> pref_store =
+ new JsonPrefStore(input_file,
+ message_loop_.message_loop_proxy().get(),
+ intercepting_pref_filter.PassAs<PrefFilter>());
+
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ // Ownership of the |mock_error_delegate| is handed to the |pref_store| below.
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+
+ {
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(0);
+ // EXPECT_CALL(*mock_error_delegate,
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_FALSE(pref_store->IsInitializationComplete());
+ EXPECT_FALSE(pref_store->GetValue(kHomePage, NULL));
+ }
+
+ {
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ // EXPECT_CALL(*mock_error_delegate,
+ // OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+
+ raw_intercepting_pref_filter_->ReleasePrefs();
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_FALSE(raw_intercepting_pref_filter_->has_intercepted_prefs());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ EXPECT_TRUE(pref_store->GetValue(kHomePage, NULL));
+ }
+
+ pref_store->RemoveObserver(&mock_observer);
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFile) {
+ ASSERT_TRUE(
+ base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("alternate.json")));
+
+ // Test that the alternate file is moved to the main file and read as-is from
+ // there.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ alternate_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
+
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFileIgnoredWhenMainFileExists) {
+ ASSERT_TRUE(
+ base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+ ASSERT_TRUE(
+ base::CopyFile(data_dir_.AppendASCII("invalid.json"),
+ temp_dir_.path().AppendASCII("alternate.json")));
+
+ // Test that the alternate file is ignored and that the read occurs from the
+ // existing main file. There is no attempt at even deleting the alternate
+ // file as this scenario should never happen in normal user-data-dirs.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ alternate_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, AlternateFileDNE) {
+ ASSERT_TRUE(
+ base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("write.json")));
+
+ // Test that the basic read works fine when an alternate file is specified but
+ // does not exist.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ alternate_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
+TEST_F(JsonPrefStoreTest, BasicAsyncWithAlternateFile) {
+ ASSERT_TRUE(
+ base::CopyFile(data_dir_.AppendASCII("read.json"),
+ temp_dir_.path().AppendASCII("alternate.json")));
+
+ // Test that the alternate file is moved to the main file and read as-is from
+ // there even when the read is made asynchronously.
+ base::FilePath input_file = temp_dir_.path().AppendASCII("write.json");
+ base::FilePath alternate_input_file =
+ temp_dir_.path().AppendASCII("alternate.json");
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+ scoped_refptr<JsonPrefStore> pref_store = new JsonPrefStore(
+ input_file,
+ alternate_input_file,
+ message_loop_.message_loop_proxy().get(),
+ scoped_ptr<PrefFilter>());
+
+ ASSERT_FALSE(PathExists(input_file));
+ ASSERT_TRUE(PathExists(alternate_input_file));
+
+ {
+ MockPrefStoreObserver mock_observer;
+ pref_store->AddObserver(&mock_observer);
+
+ MockReadErrorDelegate* mock_error_delegate = new MockReadErrorDelegate;
+ pref_store->ReadPrefsAsync(mock_error_delegate);
+
+ EXPECT_CALL(mock_observer, OnInitializationCompleted(true)).Times(1);
+ EXPECT_CALL(*mock_error_delegate,
+ OnError(PersistentPrefStore::PREF_READ_ERROR_NONE)).Times(0);
+ RunLoop().RunUntilIdle();
+ pref_store->RemoveObserver(&mock_observer);
+
+ EXPECT_FALSE(pref_store->ReadOnly());
+ EXPECT_TRUE(pref_store->IsInitializationComplete());
+ }
+
+ ASSERT_TRUE(PathExists(input_file));
+ ASSERT_FALSE(PathExists(alternate_input_file));
+
+ // The JSON file looks like this:
+ // {
+ // "homepage": "http://www.cnn.com",
+ // "some_directory": "/usr/local/",
+ // "tabs": {
+ // "new_windows_in_tabs": true,
+ // "max_tabs": 20
+ // }
+ // }
+
+ RunBasicJsonPrefStoreTest(
+ pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
+}
+
} // namespace base
diff --git a/chromium/base/prefs/overlay_user_pref_store_unittest.cc b/chromium/base/prefs/overlay_user_pref_store_unittest.cc
index c4e980bbae3..18e9a5c9910 100644
--- a/chromium/base/prefs/overlay_user_pref_store_unittest.cc
+++ b/chromium/base/prefs/overlay_user_pref_store_unittest.cc
@@ -48,47 +48,39 @@ TEST_F(OverlayUserPrefStoreTest, Observer) {
overlay_->AddObserver(&obs);
// Check that underlay first value is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
underlay_->SetValue(overlay_key, new FundamentalValue(42));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(overlay_key);
// Check that underlay overwriting is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
underlay_->SetValue(overlay_key, new FundamentalValue(43));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(overlay_key);
// Check that overwriting change in overlay is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
overlay_->SetValue(overlay_key, new FundamentalValue(44));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(overlay_key);
// Check that hidden underlay change is not reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
underlay_->SetValue(overlay_key, new FundamentalValue(45));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
// Check that overlay remove is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
overlay_->RemoveValue(overlay_key);
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(overlay_key);
// Check that underlay remove is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(1);
underlay_->RemoveValue(overlay_key);
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(overlay_key);
// Check respecting of silence.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
overlay_->SetValueSilently(overlay_key, new FundamentalValue(46));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(overlay_key))).Times(0);
underlay_->SetValue(overlay_key, new FundamentalValue(47));
overlay_->SetValue(overlay_key, new FundamentalValue(48));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
}
TEST_F(OverlayUserPrefStoreTest, GetAndSet) {
@@ -154,23 +146,20 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
const Value* value = NULL;
// Check that underlay first value is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
underlay_->SetValue(regular_key, new FundamentalValue(42));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that underlay overwriting is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
underlay_->SetValue(regular_key, new FundamentalValue(43));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that we get this value from the overlay
EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that overwriting change in overlay is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
overlay_->SetValue(regular_key, new FundamentalValue(44));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that we get this value from the overlay and the underlay.
EXPECT_TRUE(overlay_->GetValue(regular_key, &value));
@@ -179,26 +168,23 @@ TEST_F(OverlayUserPrefStoreTest, GlobalPref) {
EXPECT_TRUE(base::FundamentalValue(44).Equals(value));
// Check that overlay remove is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(1);
overlay_->RemoveValue(regular_key);
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(regular_key);
// Check that value was removed from overlay and underlay
EXPECT_FALSE(overlay_->GetValue(regular_key, &value));
EXPECT_FALSE(underlay_->GetValue(regular_key, &value));
// Check respecting of silence.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0);
overlay_->SetValueSilently(regular_key, new FundamentalValue(46));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(regular_key))).Times(0);
underlay_->SetValue(regular_key, new FundamentalValue(47));
overlay_->SetValue(regular_key, new FundamentalValue(48));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
}
// Check that names mapping works correctly.
@@ -210,14 +196,12 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) {
// Check that if there is no override in the overlay, changing underlay value
// is reported as changing an overlay value.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
underlay_->SetValue(mapped_underlay_key, new FundamentalValue(42));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that underlay overwriting is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
underlay_->SetValue(mapped_underlay_key, new FundamentalValue(43));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that we get this value from the overlay with both keys
EXPECT_TRUE(overlay_->GetValue(mapped_overlay_key, &value));
@@ -227,9 +211,8 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) {
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that overwriting change in overlay is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
overlay_->SetValue(mapped_overlay_key, new FundamentalValue(44));
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that we get an overriden value from overlay, while reading the
// value from underlay still holds an old value.
@@ -241,38 +224,31 @@ TEST_F(OverlayUserPrefStoreTest, NamesMapping) {
EXPECT_TRUE(base::FundamentalValue(43).Equals(value));
// Check that hidden underlay change is not reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
underlay_->SetValue(mapped_underlay_key, new FundamentalValue(45));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
// Check that overlay remove is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
overlay_->RemoveValue(mapped_overlay_key);
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that underlay remove is reported.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(1);
underlay_->RemoveValue(mapped_underlay_key);
- Mock::VerifyAndClearExpectations(&obs);
+ obs.VerifyAndResetChangedKey(mapped_overlay_key);
// Check that value was removed.
EXPECT_FALSE(overlay_->GetValue(mapped_overlay_key, &value));
EXPECT_FALSE(overlay_->GetValue(mapped_underlay_key, &value));
// Check respecting of silence.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0);
overlay_->SetValueSilently(mapped_overlay_key, new FundamentalValue(46));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
overlay_->RemoveObserver(&obs);
// Check successful unsubscription.
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_overlay_key))).Times(0);
- EXPECT_CALL(obs, OnPrefValueChanged(StrEq(mapped_underlay_key))).Times(0);
underlay_->SetValue(mapped_underlay_key, new FundamentalValue(47));
overlay_->SetValue(mapped_overlay_key, new FundamentalValue(48));
- Mock::VerifyAndClearExpectations(&obs);
+ EXPECT_TRUE(obs.changed_keys.empty());
}
} // namespace base
diff --git a/chromium/base/prefs/persistent_pref_store.h b/chromium/base/prefs/persistent_pref_store.h
index 811ebff7014..fb9d6dcdab6 100644
--- a/chromium/base/prefs/persistent_pref_store.h
+++ b/chromium/base/prefs/persistent_pref_store.h
@@ -8,28 +8,34 @@
#include <string>
#include "base/prefs/base_prefs_export.h"
-#include "base/prefs/pref_store.h"
+#include "base/prefs/writeable_pref_store.h"
// This interface is complementary to the PrefStore interface, declaring
// additional functionality that adds support for setting values and persisting
// the data to some backing store.
-class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore {
+class BASE_PREFS_EXPORT PersistentPrefStore : public WriteablePrefStore {
public:
// Unique integer code for each type of error so we can report them
// distinctly in a histogram.
- // NOTE: Don't change the order here as it will change the server's meaning
- // of the histogram.
+ // NOTE: Don't change the explicit values of the enums as it will change the
+ // server's meaning of the histogram.
enum PrefReadError {
PREF_READ_ERROR_NONE = 0,
- PREF_READ_ERROR_JSON_PARSE,
- PREF_READ_ERROR_JSON_TYPE,
- PREF_READ_ERROR_ACCESS_DENIED,
- PREF_READ_ERROR_FILE_OTHER,
- PREF_READ_ERROR_FILE_LOCKED,
- PREF_READ_ERROR_NO_FILE,
- PREF_READ_ERROR_JSON_REPEAT,
- PREF_READ_ERROR_OTHER,
- PREF_READ_ERROR_FILE_NOT_SPECIFIED,
+ PREF_READ_ERROR_JSON_PARSE = 1,
+ PREF_READ_ERROR_JSON_TYPE = 2,
+ PREF_READ_ERROR_ACCESS_DENIED = 3,
+ PREF_READ_ERROR_FILE_OTHER = 4,
+ PREF_READ_ERROR_FILE_LOCKED = 5,
+ PREF_READ_ERROR_NO_FILE = 6,
+ PREF_READ_ERROR_JSON_REPEAT = 7,
+ // PREF_READ_ERROR_OTHER = 8, // Deprecated.
+ PREF_READ_ERROR_FILE_NOT_SPECIFIED = 9,
+ // Indicates that ReadPrefs() couldn't complete synchronously and is waiting
+ // for an asynchronous task to complete first.
+ PREF_READ_ERROR_ASYNCHRONOUS_TASK_INCOMPLETE = 10,
+ PREF_READ_ERROR_LEVELDB_IO = 11,
+ PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY = 12,
+ PREF_READ_ERROR_LEVELDB_CORRUPTION = 13,
PREF_READ_ERROR_MAX_ENUM
};
@@ -40,29 +46,12 @@ class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore {
virtual void OnError(PrefReadError error) = 0;
};
- // Equivalent to PrefStore::GetValue but returns a mutable value.
- virtual bool GetMutableValue(const std::string& key,
- base::Value** result) = 0;
-
- // Triggers a value changed notification. This function needs to be called
- // if one retrieves a list or dictionary with GetMutableValue and change its
- // value. SetValue takes care of notifications itself. Note that
- // ReportValueChanged will trigger notifications even if nothing has changed.
- virtual void ReportValueChanged(const std::string& key) = 0;
-
- // Sets a |value| for |key| in the store. Assumes ownership of |value|, which
- // must be non-NULL.
- virtual void SetValue(const std::string& key, base::Value* value) = 0;
-
// Same as SetValue, but doesn't generate notifications. This is used by
// PrefService::GetMutableUserPref() in order to put empty entries
// into the user pref store. Using SetValue is not an option since existing
// tests rely on the number of notifications generated.
virtual void SetValueSilently(const std::string& key, base::Value* value) = 0;
- // Removes the value for |key|.
- virtual void RemoveValue(const std::string& key) = 0;
-
// Whether the store is in a pseudo-read-only mode where changes are not
// actually persisted to disk. This happens in some cases when there are
// read errors during startup.
diff --git a/chromium/base/prefs/pref_filter.h b/chromium/base/prefs/pref_filter.h
new file mode 100644
index 00000000000..82a44c6ef9b
--- /dev/null
+++ b/chromium/base/prefs/pref_filter.h
@@ -0,0 +1,55 @@
+// Copyright 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_PREFS_PREF_FILTER_H_
+#define BASE_PREFS_PREF_FILTER_H_
+
+#include <string>
+
+#include "base/callback_forward.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/prefs/base_prefs_export.h"
+
+namespace base {
+class DictionaryValue;
+class Value;
+} // namespace base
+
+// Filters preferences as they are loaded from disk or updated at runtime.
+// Currently supported only by JsonPrefStore.
+class BASE_PREFS_EXPORT PrefFilter {
+ public:
+ // A callback to be invoked when |prefs| have been read (and possibly
+ // pre-modified) and are now ready to be handed back to this callback's
+ // builder. |schedule_write| indicates whether a write should be immediately
+ // scheduled (typically because the |prefs| were pre-modified).
+ typedef base::Callback<void(scoped_ptr<base::DictionaryValue> prefs,
+ bool schedule_write)> PostFilterOnLoadCallback;
+
+ virtual ~PrefFilter() {}
+
+ // This method is given ownership of the |pref_store_contents| read from disk
+ // before the underlying PersistentPrefStore gets to use them. It must hand
+ // them back via |post_filter_on_load_callback|, but may modify them first.
+ // Note: This method is asynchronous, which may make calls like
+ // PersistentPrefStore::ReadPrefs() asynchronous. The owner of filtered
+ // PersistentPrefStores should handle this to make the reads look synchronous
+ // to external users (see SegregatedPrefStore::ReadPrefs() for an example).
+ virtual void FilterOnLoad(
+ const PostFilterOnLoadCallback& post_filter_on_load_callback,
+ scoped_ptr<base::DictionaryValue> pref_store_contents) = 0;
+
+ // Receives notification when a pref store value is changed, before Observers
+ // are notified.
+ virtual void FilterUpdate(const std::string& path) = 0;
+
+ // Receives notification when the pref store is about to serialize data
+ // contained in |pref_store_contents| to a string. Modifications to
+ // |pref_store_contents| will be persisted to disk and also affect the
+ // in-memory state.
+ virtual void FilterSerializeData(
+ base::DictionaryValue* pref_store_contents) = 0;
+};
+
+#endif // BASE_PREFS_PREF_FILTER_H_
diff --git a/chromium/base/prefs/pref_notifier_impl_unittest.cc b/chromium/base/prefs/pref_notifier_impl_unittest.cc
index 29ea322e7ed..8482c0289c7 100644
--- a/chromium/base/prefs/pref_notifier_impl_unittest.cc
+++ b/chromium/base/prefs/pref_notifier_impl_unittest.cc
@@ -80,13 +80,6 @@ class PrefObserverMock : public PrefObserver {
virtual ~PrefObserverMock() {}
MOCK_METHOD2(OnPreferenceChanged, void(PrefService*, const std::string&));
-
- void Expect(PrefService* prefs,
- const std::string& pref_name,
- const base::Value* value) {
- EXPECT_CALL(*this, OnPreferenceChanged(prefs, pref_name))
- .With(PrefValueMatches(prefs, pref_name, value));
- }
};
// Test fixture class.
diff --git a/chromium/base/prefs/pref_registry.cc b/chromium/base/prefs/pref_registry.cc
index 9d6b05c8362..134996185c4 100644
--- a/chromium/base/prefs/pref_registry.cc
+++ b/chromium/base/prefs/pref_registry.cc
@@ -31,13 +31,11 @@ PrefRegistry::const_iterator PrefRegistry::end() const {
void PrefRegistry::SetDefaultPrefValue(const char* pref_name,
base::Value* value) {
DCHECK(value);
- if (DCHECK_IS_ON()) {
- const base::Value* current_value = NULL;
- DCHECK(defaults_->GetValue(pref_name, &current_value))
- << "Setting default for unregistered pref: " << pref_name;
- DCHECK(value->IsType(current_value->GetType()))
- << "Wrong type for new default: " << pref_name;
- }
+ const base::Value* current_value = NULL;
+ DCHECK(defaults_->GetValue(pref_name, &current_value))
+ << "Setting default for unregistered pref: " << pref_name;
+ DCHECK(value->IsType(current_value->GetType()))
+ << "Wrong type for new default: " << pref_name;
defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value));
}
diff --git a/chromium/base/prefs/pref_registry_simple.cc b/chromium/base/prefs/pref_registry_simple.cc
index c068b4ba43a..7453016a272 100644
--- a/chromium/base/prefs/pref_registry_simple.cc
+++ b/chromium/base/prefs/pref_registry_simple.cc
@@ -16,29 +16,28 @@ PrefRegistrySimple::~PrefRegistrySimple() {
void PrefRegistrySimple::RegisterBooleanPref(const char* path,
bool default_value) {
- RegisterPreference(path, base::Value::CreateBooleanValue(default_value));
+ RegisterPreference(path, new base::FundamentalValue(default_value));
}
void PrefRegistrySimple::RegisterIntegerPref(const char* path,
int default_value) {
- RegisterPreference(path, base::Value::CreateIntegerValue(default_value));
+ RegisterPreference(path, new base::FundamentalValue(default_value));
}
void PrefRegistrySimple::RegisterDoublePref(const char* path,
double default_value) {
- RegisterPreference(path, base::Value::CreateDoubleValue(default_value));
+ RegisterPreference(path, new base::FundamentalValue(default_value));
}
void PrefRegistrySimple::RegisterStringPref(const char* path,
const std::string& default_value) {
- RegisterPreference(path, base::Value::CreateStringValue(default_value));
+ RegisterPreference(path, new base::StringValue(default_value));
}
void PrefRegistrySimple::RegisterFilePathPref(
const char* path,
const base::FilePath& default_value) {
- RegisterPreference(path,
- base::Value::CreateStringValue(default_value.value()));
+ RegisterPreference(path, new base::StringValue(default_value.value()));
}
void PrefRegistrySimple::RegisterListPref(const char* path) {
@@ -63,5 +62,5 @@ void PrefRegistrySimple::RegisterDictionaryPref(
void PrefRegistrySimple::RegisterInt64Pref(const char* path,
int64 default_value) {
RegisterPreference(
- path, base::Value::CreateStringValue(base::Int64ToString(default_value)));
+ path, new base::StringValue(base::Int64ToString(default_value)));
}
diff --git a/chromium/base/prefs/pref_service.cc b/chromium/base/prefs/pref_service.cc
index 576043b5489..a8e56d0b370 100644
--- a/chromium/base/prefs/pref_service.cc
+++ b/chromium/base/prefs/pref_service.cc
@@ -67,7 +67,9 @@ PrefService::~PrefService() {
}
void PrefService::InitFromStorage(bool async) {
- if (!async) {
+ if (user_pref_store_->IsInitializationComplete()) {
+ read_error_callback_.Run(user_pref_store_->GetReadError());
+ } else if (!async) {
read_error_callback_.Run(user_pref_store_->ReadPrefs());
} else {
// Guarantee that initialization happens after this function returned.
@@ -336,19 +338,19 @@ void PrefService::Set(const char* path, const base::Value& value) {
}
void PrefService::SetBoolean(const char* path, bool value) {
- SetUserPrefValue(path, base::Value::CreateBooleanValue(value));
+ SetUserPrefValue(path, new base::FundamentalValue(value));
}
void PrefService::SetInteger(const char* path, int value) {
- SetUserPrefValue(path, base::Value::CreateIntegerValue(value));
+ SetUserPrefValue(path, new base::FundamentalValue(value));
}
void PrefService::SetDouble(const char* path, double value) {
- SetUserPrefValue(path, base::Value::CreateDoubleValue(value));
+ SetUserPrefValue(path, new base::FundamentalValue(value));
}
void PrefService::SetString(const char* path, const std::string& value) {
- SetUserPrefValue(path, base::Value::CreateStringValue(value));
+ SetUserPrefValue(path, new base::StringValue(value));
}
void PrefService::SetFilePath(const char* path, const base::FilePath& value) {
@@ -356,8 +358,7 @@ void PrefService::SetFilePath(const char* path, const base::FilePath& value) {
}
void PrefService::SetInt64(const char* path, int64 value) {
- SetUserPrefValue(path,
- base::Value::CreateStringValue(base::Int64ToString(value)));
+ SetUserPrefValue(path, new base::StringValue(base::Int64ToString(value)));
}
int64 PrefService::GetInt64(const char* path) const {
@@ -378,8 +379,7 @@ int64 PrefService::GetInt64(const char* path) const {
}
void PrefService::SetUint64(const char* path, uint64 value) {
- SetUserPrefValue(path,
- base::Value::CreateStringValue(base::Uint64ToString(value)));
+ SetUserPrefValue(path, new base::StringValue(base::Uint64ToString(value)));
}
uint64 PrefService::GetUint64(const char* path) const {
diff --git a/chromium/base/prefs/pref_service_factory.cc b/chromium/base/prefs/pref_service_factory.cc
index 9c598530c1b..d644cb1c2b0 100644
--- a/chromium/base/prefs/pref_service_factory.cc
+++ b/chromium/base/prefs/pref_service_factory.cc
@@ -7,6 +7,7 @@
#include "base/bind.h"
#include "base/prefs/default_pref_store.h"
#include "base/prefs/json_pref_store.h"
+#include "base/prefs/pref_filter.h"
#include "base/prefs/pref_notifier_impl.h"
#include "base/prefs/pref_service.h"
@@ -37,7 +38,8 @@ PrefServiceFactory::~PrefServiceFactory() {}
void PrefServiceFactory::SetUserPrefsFile(
const base::FilePath& prefs_file,
base::SequencedTaskRunner* task_runner) {
- user_prefs_ = new JsonPrefStore(prefs_file, task_runner);
+ user_prefs_ = new JsonPrefStore(
+ prefs_file, task_runner, scoped_ptr<PrefFilter>());
}
scoped_ptr<PrefService> PrefServiceFactory::Create(
diff --git a/chromium/base/prefs/pref_service_unittest.cc b/chromium/base/prefs/pref_service_unittest.cc
index ff6bb2f2de3..36ad887df2c 100644
--- a/chromium/base/prefs/pref_service_unittest.cc
+++ b/chromium/base/prefs/pref_service_unittest.cc
@@ -79,7 +79,7 @@ TEST(PrefServiceTest, Observers) {
TestingPrefServiceSimple prefs;
prefs.SetUserPref(pref_name,
- base::Value::CreateStringValue("http://www.cnn.com"));
+ new base::StringValue("http://www.cnn.com"));
prefs.registry()->RegisterStringPref(pref_name, std::string());
const char new_pref_value[] = "http://www.google.com/";
@@ -138,7 +138,7 @@ TEST(PrefServiceTest, GetValueChangedType) {
// Check falling back to a recommended value.
prefs.SetUserPref(kPrefName,
- base::Value::CreateStringValue("not an integer"));
+ new base::StringValue("not an integer"));
const PrefService::Preference* pref = prefs.FindPreference(kPrefName);
ASSERT_TRUE(pref);
const base::Value* value = pref->GetValue();
@@ -173,7 +173,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
ASSERT_FALSE(value);
// Set a user-set value.
- prefs.SetUserPref(kPrefName, base::Value::CreateIntegerValue(kUserValue));
+ prefs.SetUserPref(kPrefName, new base::FundamentalValue(kUserValue));
// Check that GetValue() returns the user-set value.
value = pref->GetValue();
@@ -189,7 +189,7 @@ TEST(PrefServiceTest, GetValueAndGetRecommendedValue) {
// Set a recommended value.
prefs.SetRecommendedPref(kPrefName,
- base::Value::CreateIntegerValue(kRecommendedValue));
+ new base::FundamentalValue(kRecommendedValue));
// Check that GetValue() returns the user-set value.
value = pref->GetValue();
@@ -302,7 +302,7 @@ TEST_F(PrefServiceSetValueTest, SetListValue) {
Mock::VerifyAndClearExpectations(&observer_);
base::ListValue new_value;
- new_value.Append(base::Value::CreateStringValue(kValue));
+ new_value.Append(new base::StringValue(kValue));
observer_.Expect(kName, &new_value);
prefs_.Set(kName, new_value);
Mock::VerifyAndClearExpectations(&observer_);
diff --git a/chromium/base/prefs/pref_store_observer_mock.cc b/chromium/base/prefs/pref_store_observer_mock.cc
index 0970e6310d7..f1a31bb985d 100644
--- a/chromium/base/prefs/pref_store_observer_mock.cc
+++ b/chromium/base/prefs/pref_store_observer_mock.cc
@@ -4,6 +4,26 @@
#include "base/prefs/pref_store_observer_mock.h"
-PrefStoreObserverMock::PrefStoreObserverMock() {}
+#include "testing/gtest/include/gtest/gtest.h"
+
+PrefStoreObserverMock::PrefStoreObserverMock()
+ : initialized(false), initialization_success(false) {}
PrefStoreObserverMock::~PrefStoreObserverMock() {}
+
+void PrefStoreObserverMock::VerifyAndResetChangedKey(
+ const std::string& expected) {
+ EXPECT_EQ(1u, changed_keys.size());
+ if (changed_keys.size() >= 1)
+ EXPECT_EQ(expected, changed_keys.front());
+ changed_keys.clear();
+}
+
+void PrefStoreObserverMock::OnPrefValueChanged(const std::string& key) {
+ changed_keys.push_back(key);
+}
+
+void PrefStoreObserverMock::OnInitializationCompleted(bool success) {
+ initialized = true;
+ initialization_success = success;
+}
diff --git a/chromium/base/prefs/pref_store_observer_mock.h b/chromium/base/prefs/pref_store_observer_mock.h
index 8252c3b3599..594807f31e0 100644
--- a/chromium/base/prefs/pref_store_observer_mock.h
+++ b/chromium/base/prefs/pref_store_observer_mock.h
@@ -5,18 +5,28 @@
#ifndef BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_
#define BASE_PREFS_PREF_STORE_OBSERVER_MOCK_H_
-#include "base/basictypes.h"
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
#include "base/prefs/pref_store.h"
-#include "testing/gmock/include/gmock/gmock.h"
-// A gmock-ified implementation of PrefStore::Observer.
+// A mock implementation of PrefStore::Observer.
class PrefStoreObserverMock : public PrefStore::Observer {
public:
PrefStoreObserverMock();
virtual ~PrefStoreObserverMock();
- MOCK_METHOD1(OnPrefValueChanged, void(const std::string&));
- MOCK_METHOD1(OnInitializationCompleted, void(bool));
+ void VerifyAndResetChangedKey(const std::string& expected);
+
+ // PrefStore::Observer implementation
+ virtual void OnPrefValueChanged(const std::string& key) OVERRIDE;
+ virtual void OnInitializationCompleted(bool success) OVERRIDE;
+
+ std::vector<std::string> changed_keys;
+ bool initialized;
+ bool initialization_success; // Only valid if |initialized|.
private:
DISALLOW_COPY_AND_ASSIGN(PrefStoreObserverMock);
diff --git a/chromium/base/prefs/scoped_user_pref_update.cc b/chromium/base/prefs/scoped_user_pref_update.cc
index c86b1634b52..41b654cd58c 100644
--- a/chromium/base/prefs/scoped_user_pref_update.cc
+++ b/chromium/base/prefs/scoped_user_pref_update.cc
@@ -20,7 +20,7 @@ ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() {
Notify();
}
-Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
+base::Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
if (!value_)
value_ = service_->GetMutableUserPref(path_.c_str(), type);
return value_;
diff --git a/chromium/base/prefs/scoped_user_pref_update_unittest.cc b/chromium/base/prefs/scoped_user_pref_update_unittest.cc
index 505526c07cf..dfe2074720d 100644
--- a/chromium/base/prefs/scoped_user_pref_update_unittest.cc
+++ b/chromium/base/prefs/scoped_user_pref_update_unittest.cc
@@ -40,13 +40,13 @@ const char ScopedUserPrefUpdateTest::kValue[] = "value";
TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
// Dictionary that will be expected to be set at the end.
- DictionaryValue expected_dictionary;
+ base::DictionaryValue expected_dictionary;
expected_dictionary.SetString(kKey, kValue);
{
EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
DictionaryPrefUpdate update(&prefs_, kPref);
- DictionaryValue* value = update.Get();
+ base::DictionaryValue* value = update.Get();
ASSERT_TRUE(value);
value->SetString(kKey, kValue);
@@ -55,7 +55,7 @@ TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
Mock::VerifyAndClearExpectations(&observer_);
// Modifications happen online and are instantly visible, though.
- const DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+ const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
ASSERT_TRUE(current_value);
EXPECT_TRUE(expected_dictionary.Equals(current_value));
@@ -64,18 +64,18 @@ TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
}
Mock::VerifyAndClearExpectations(&observer_);
- const DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+ const base::DictionaryValue* current_value = prefs_.GetDictionary(kPref);
ASSERT_TRUE(current_value);
EXPECT_TRUE(expected_dictionary.Equals(current_value));
}
TEST_F(ScopedUserPrefUpdateTest, NeverTouchAnything) {
- const DictionaryValue* old_value = prefs_.GetDictionary(kPref);
+ const base::DictionaryValue* old_value = prefs_.GetDictionary(kPref);
EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
{
DictionaryPrefUpdate update(&prefs_, kPref);
}
- const DictionaryValue* new_value = prefs_.GetDictionary(kPref);
+ const base::DictionaryValue* new_value = prefs_.GetDictionary(kPref);
EXPECT_EQ(old_value, new_value);
Mock::VerifyAndClearExpectations(&observer_);
}
diff --git a/chromium/base/prefs/testing_pref_store.cc b/chromium/base/prefs/testing_pref_store.cc
index 2f429c9a1a9..f20824e1733 100644
--- a/chromium/base/prefs/testing_pref_store.cc
+++ b/chromium/base/prefs/testing_pref_store.cc
@@ -9,8 +9,12 @@
TestingPrefStore::TestingPrefStore()
: read_only_(true),
- init_complete_(false) {
-}
+ read_success_(true),
+ read_error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
+ block_async_read_(false),
+ pending_async_read_(false),
+ init_complete_(false),
+ committed_(true) {}
bool TestingPrefStore::GetValue(const std::string& key,
const base::Value** value) const {
@@ -39,18 +43,23 @@ bool TestingPrefStore::IsInitializationComplete() const {
}
void TestingPrefStore::SetValue(const std::string& key, base::Value* value) {
- if (prefs_.SetValue(key, value))
+ if (prefs_.SetValue(key, value)) {
+ committed_ = false;
NotifyPrefValueChanged(key);
+ }
}
void TestingPrefStore::SetValueSilently(const std::string& key,
base::Value* value) {
- prefs_.SetValue(key, value);
+ if (prefs_.SetValue(key, value))
+ committed_ = false;
}
void TestingPrefStore::RemoveValue(const std::string& key) {
- if (prefs_.RemoveValue(key))
+ if (prefs_.RemoveValue(key)) {
+ committed_ = false;
NotifyPrefValueChanged(key);
+ }
}
bool TestingPrefStore::ReadOnly() const {
@@ -58,21 +67,26 @@ bool TestingPrefStore::ReadOnly() const {
}
PersistentPrefStore::PrefReadError TestingPrefStore::GetReadError() const {
- return PersistentPrefStore::PREF_READ_ERROR_NONE;
+ return read_error_;
}
PersistentPrefStore::PrefReadError TestingPrefStore::ReadPrefs() {
NotifyInitializationCompleted();
- return PersistentPrefStore::PREF_READ_ERROR_NONE;
+ return read_error_;
}
-void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate_raw) {
- scoped_ptr<ReadErrorDelegate> error_delegate(error_delegate_raw);
- NotifyInitializationCompleted();
+void TestingPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
+ DCHECK(!pending_async_read_);
+ error_delegate_.reset(error_delegate);
+ if (block_async_read_)
+ pending_async_read_ = true;
+ else
+ NotifyInitializationCompleted();
}
+void TestingPrefStore::CommitPendingWrite() { committed_ = true; }
+
void TestingPrefStore::SetInitializationCompleted() {
- init_complete_ = true;
NotifyInitializationCompleted();
}
@@ -81,7 +95,12 @@ void TestingPrefStore::NotifyPrefValueChanged(const std::string& key) {
}
void TestingPrefStore::NotifyInitializationCompleted() {
- FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true));
+ DCHECK(!init_complete_);
+ init_complete_ = true;
+ if (read_success_ && read_error_ != PREF_READ_ERROR_NONE && error_delegate_)
+ error_delegate_->OnError(read_error_);
+ FOR_EACH_OBSERVER(
+ Observer, observers_, OnInitializationCompleted(read_success_));
}
void TestingPrefStore::ReportValueChanged(const std::string& key) {
@@ -126,8 +145,26 @@ bool TestingPrefStore::GetBoolean(const std::string& key, bool* value) const {
return stored_value->GetAsBoolean(value);
}
+void TestingPrefStore::SetBlockAsyncRead(bool block_async_read) {
+ DCHECK(!init_complete_);
+ block_async_read_ = block_async_read;
+ if (pending_async_read_ && !block_async_read_)
+ NotifyInitializationCompleted();
+}
+
void TestingPrefStore::set_read_only(bool read_only) {
read_only_ = read_only;
}
+void TestingPrefStore::set_read_success(bool read_success) {
+ DCHECK(!init_complete_);
+ read_success_ = read_success;
+}
+
+void TestingPrefStore::set_read_error(
+ PersistentPrefStore::PrefReadError read_error) {
+ DCHECK(!init_complete_);
+ read_error_ = read_error;
+}
+
TestingPrefStore::~TestingPrefStore() {}
diff --git a/chromium/base/prefs/testing_pref_store.h b/chromium/base/prefs/testing_pref_store.h
index c6a2b8336f0..785f935539e 100644
--- a/chromium/base/prefs/testing_pref_store.h
+++ b/chromium/base/prefs/testing_pref_store.h
@@ -40,7 +40,7 @@ class TestingPrefStore : public PersistentPrefStore {
virtual PrefReadError GetReadError() const OVERRIDE;
virtual PersistentPrefStore::PrefReadError ReadPrefs() OVERRIDE;
virtual void ReadPrefsAsync(ReadErrorDelegate* error_delegate) OVERRIDE;
- virtual void CommitPendingWrite() OVERRIDE {}
+ virtual void CommitPendingWrite() OVERRIDE;
// Marks the store as having completed initialization.
void SetInitializationCompleted();
@@ -58,9 +58,18 @@ class TestingPrefStore : public PersistentPrefStore {
bool GetInteger(const std::string& key, int* value) const;
bool GetBoolean(const std::string& key, bool* value) const;
+ // Determines whether ReadPrefsAsync completes immediately. Defaults to false
+ // (non-blocking). To block, invoke this with true (blocking) before the call
+ // to ReadPrefsAsync. To unblock, invoke again with false (non-blocking) after
+ // the call to ReadPrefsAsync.
+ void SetBlockAsyncRead(bool block_async_read);
+
// Getter and Setter methods for setting and getting the state of the
// |TestingPrefStore|.
virtual void set_read_only(bool read_only);
+ void set_read_success(bool read_success);
+ void set_read_error(PersistentPrefStore::PrefReadError read_error);
+ bool committed() { return committed_; }
protected:
virtual ~TestingPrefStore();
@@ -72,9 +81,26 @@ class TestingPrefStore : public PersistentPrefStore {
// Flag that indicates if the PrefStore is read-only
bool read_only_;
+ // The result to pass to PrefStore::Observer::OnInitializationCompleted
+ bool read_success_;
+
+ // The result to return from ReadPrefs or ReadPrefsAsync.
+ PersistentPrefStore::PrefReadError read_error_;
+
+ // Whether a call to ReadPrefsAsync should block.
+ bool block_async_read_;
+
+ // Whether there is a pending call to ReadPrefsAsync.
+ bool pending_async_read_;
+
// Whether initialization has been completed.
bool init_complete_;
+ // Whether the store contents have been committed to disk since the last
+ // mutation.
+ bool committed_;
+
+ scoped_ptr<ReadErrorDelegate> error_delegate_;
ObserverList<PrefStore::Observer, true> observers_;
DISALLOW_COPY_AND_ASSIGN(TestingPrefStore);
diff --git a/chromium/base/prefs/value_map_pref_store.cc b/chromium/base/prefs/value_map_pref_store.cc
index 750688c373f..5e890e2589f 100644
--- a/chromium/base/prefs/value_map_pref_store.cc
+++ b/chromium/base/prefs/value_map_pref_store.cc
@@ -28,8 +28,6 @@ bool ValueMapPrefStore::HasObservers() const {
return observers_.might_have_observers();
}
-ValueMapPrefStore::~ValueMapPrefStore() {}
-
void ValueMapPrefStore::SetValue(const std::string& key, base::Value* value) {
if (prefs_.SetValue(key, value))
FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
@@ -40,6 +38,17 @@ void ValueMapPrefStore::RemoveValue(const std::string& key) {
FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
}
+bool ValueMapPrefStore::GetMutableValue(const std::string& key,
+ base::Value** value) {
+ return prefs_.GetValue(key, value);
+}
+
+void ValueMapPrefStore::ReportValueChanged(const std::string& key) {
+ FOR_EACH_OBSERVER(Observer, observers_, OnPrefValueChanged(key));
+}
+
+ValueMapPrefStore::~ValueMapPrefStore() {}
+
void ValueMapPrefStore::NotifyInitializationCompleted() {
FOR_EACH_OBSERVER(Observer, observers_, OnInitializationCompleted(true));
}
diff --git a/chromium/base/prefs/value_map_pref_store.h b/chromium/base/prefs/value_map_pref_store.h
index 21e4b888175..c202725d947 100644
--- a/chromium/base/prefs/value_map_pref_store.h
+++ b/chromium/base/prefs/value_map_pref_store.h
@@ -11,12 +11,12 @@
#include "base/basictypes.h"
#include "base/observer_list.h"
#include "base/prefs/base_prefs_export.h"
-#include "base/prefs/pref_store.h"
#include "base/prefs/pref_value_map.h"
+#include "base/prefs/writeable_pref_store.h"
// A basic PrefStore implementation that uses a simple name-value map for
// storing the preference values.
-class BASE_PREFS_EXPORT ValueMapPrefStore : public PrefStore {
+class BASE_PREFS_EXPORT ValueMapPrefStore : public WriteablePrefStore {
public:
ValueMapPrefStore();
@@ -27,17 +27,16 @@ class BASE_PREFS_EXPORT ValueMapPrefStore : public PrefStore {
virtual void RemoveObserver(PrefStore::Observer* observer) OVERRIDE;
virtual bool HasObservers() const OVERRIDE;
+ // WriteablePrefStore overrides:
+ virtual void SetValue(const std::string& key, base::Value* value) OVERRIDE;
+ virtual void RemoveValue(const std::string& key) OVERRIDE;
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** value) OVERRIDE;
+ virtual void ReportValueChanged(const std::string& key) OVERRIDE;
+
protected:
virtual ~ValueMapPrefStore();
- // Store a |value| for |key| in the store. Also generates an notification if
- // the value changed. Assumes ownership of |value|, which must be non-NULL.
- void SetValue(const std::string& key, base::Value* value);
-
- // Remove the value for |key| from the store. Sends a notification if there
- // was a value to be removed.
- void RemoveValue(const std::string& key);
-
// Notify observers about the initialization completed event.
void NotifyInitializationCompleted();
diff --git a/chromium/base/prefs/writeable_pref_store.h b/chromium/base/prefs/writeable_pref_store.h
new file mode 100644
index 00000000000..908d867dbf5
--- /dev/null
+++ b/chromium/base/prefs/writeable_pref_store.h
@@ -0,0 +1,47 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PREFS_WRITEABLE_PREF_STORE_H_
+#define BASE_PREFS_WRITEABLE_PREF_STORE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/prefs/pref_store.h"
+
+namespace base {
+class Value;
+}
+
+// A pref store that can be written to as well as read from.
+class BASE_PREFS_EXPORT WriteablePrefStore : public PrefStore {
+ public:
+ WriteablePrefStore() {}
+
+ // Sets a |value| for |key| in the store. Assumes ownership of |value|, which
+ // must be non-NULL.
+ virtual void SetValue(const std::string& key, base::Value* value) = 0;
+
+ // Removes the value for |key|.
+ virtual void RemoveValue(const std::string& key) = 0;
+
+ // Equivalent to PrefStore::GetValue but returns a mutable value.
+ virtual bool GetMutableValue(const std::string& key,
+ base::Value** result) = 0;
+
+ // Triggers a value changed notification. This function needs to be called
+ // if one retrieves a list or dictionary with GetMutableValue and change its
+ // value. SetValue takes care of notifications itself. Note that
+ // ReportValueChanged will trigger notifications even if nothing has changed.
+ virtual void ReportValueChanged(const std::string& key) = 0;
+
+
+ protected:
+ virtual ~WriteablePrefStore() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WriteablePrefStore);
+};
+
+#endif // BASE_PREFS_WRITEABLE_PREF_STORE_H_
diff --git a/chromium/base/process/internal_linux.cc b/chromium/base/process/internal_linux.cc
index b1d1b6f174e..57a3ab4271a 100644
--- a/chromium/base/process/internal_linux.cc
+++ b/chromium/base/process/internal_linux.cc
@@ -115,13 +115,13 @@ void ParseProcStat(const std::string& contents, ProcStatMap* output) {
}
}
-int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats,
- ProcStatsFields field_num) {
+int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num) {
DCHECK_GE(field_num, VM_PPID);
CHECK_LT(static_cast<size_t>(field_num), proc_stats.size());
- int value;
- return StringToInt(proc_stats[field_num], &value) ? value : 0;
+ int64 value;
+ return StringToInt64(proc_stats[field_num], &value) ? value : 0;
}
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
@@ -133,15 +133,14 @@ size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
return StringToSizeT(proc_stats[field_num], &value) ? value : 0;
}
-int ReadProcStatsAndGetFieldAsInt(pid_t pid,
- ProcStatsFields field_num) {
+int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num) {
std::string stats_data;
if (!ReadProcStats(pid, &stats_data))
return 0;
std::vector<std::string> proc_stats;
if (!ParseProcStats(stats_data, &proc_stats))
return 0;
- return GetProcStatsFieldAsInt(proc_stats, field_num);
+ return GetProcStatsFieldAsInt64(proc_stats, field_num);
}
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h
index 0cacd3f3a84..5fc33566607 100644
--- a/chromium/base/process/internal_linux.h
+++ b/chromium/base/process/internal_linux.h
@@ -63,19 +63,18 @@ enum ProcStatsFields {
// Reads the |field_num|th field from |proc_stats|. Returns 0 on failure.
// This version does not handle the first 3 values, since the first value is
// simply |pid|, and the next two values are strings.
-int GetProcStatsFieldAsInt(const std::vector<std::string>& proc_stats,
- ProcStatsFields field_num);
+int64 GetProcStatsFieldAsInt64(const std::vector<std::string>& proc_stats,
+ ProcStatsFields field_num);
-// Same as GetProcStatsFieldAsInt(), but for size_t values.
+// Same as GetProcStatsFieldAsInt64(), but for size_t values.
size_t GetProcStatsFieldAsSizeT(const std::vector<std::string>& proc_stats,
ProcStatsFields field_num);
-// Convenience wrapper around GetProcStatsFieldAsInt(), ParseProcStats() and
-// ReadProcStats(). See GetProcStatsFieldAsInt() for details.
-int ReadProcStatsAndGetFieldAsInt(pid_t pid,
- ProcStatsFields field_num);
+// Convenience wrapper around GetProcStatsFieldAsInt64(), ParseProcStats() and
+// ReadProcStats(). See GetProcStatsFieldAsInt64() for details.
+int64 ReadProcStatsAndGetFieldAsInt64(pid_t pid, ProcStatsFields field_num);
-// Same as ReadProcStatsAndGetFieldAsInt() but for size_t values.
+// Same as ReadProcStatsAndGetFieldAsInt64() but for size_t values.
size_t ReadProcStatsAndGetFieldAsSizeT(pid_t pid,
ProcStatsFields field_num);
diff --git a/chromium/base/process/kill_mac.cc b/chromium/base/process/kill_mac.cc
index 5ebca5d0076..1589a641a04 100644
--- a/chromium/base/process/kill_mac.cc
+++ b/chromium/base/process/kill_mac.cc
@@ -10,6 +10,7 @@
#include <sys/wait.h>
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
@@ -76,15 +77,13 @@ void WaitForChildToDie(pid_t child, int timeout) {
int result;
- int kq = HANDLE_EINTR(kqueue());
- if (kq == -1) {
+ ScopedFD kq(HANDLE_EINTR(kqueue()));
+ if (!kq.is_valid()) {
DPLOG(ERROR) << "kqueue()";
} else {
- file_util::ScopedFD auto_close_kq(&kq);
-
struct kevent change = {0};
EV_SET(&change, child, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+ result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
if (result == -1) {
if (errno != ESRCH) {
@@ -120,7 +119,7 @@ void WaitForChildToDie(pid_t child, int timeout) {
struct kevent event = {0};
while (remaining_delta.InMilliseconds() > 0) {
const struct timespec remaining_timespec = remaining_delta.ToTimeSpec();
- result = kevent(kq, NULL, 0, &event, 1, &remaining_timespec);
+ result = kevent(kq.get(), NULL, 0, &event, 1, &remaining_timespec);
if (result == -1 && errno == EINTR) {
remaining_delta = deadline - TimeTicks::Now();
result = 0;
diff --git a/chromium/base/process/kill_posix.cc b/chromium/base/process/kill_posix.cc
index 99d70d9ab55..18c14fd3a77 100644
--- a/chromium/base/process/kill_posix.cc
+++ b/chromium/base/process/kill_posix.cc
@@ -10,6 +10,7 @@
#include <unistd.h>
#include "base/file_util.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_iterator.h"
@@ -21,11 +22,11 @@ namespace base {
namespace {
-int WaitpidWithTimeout(ProcessHandle handle,
- int64 wait_milliseconds,
- bool* success) {
+bool WaitpidWithTimeout(ProcessHandle handle,
+ int* status,
+ base::TimeDelta wait) {
// This POSIX version of this function only guarantees that we wait no less
- // than |wait_milliseconds| for the process to exit. The child process may
+ // than |wait| for the process to exit. The child process may
// exit sometime before the timeout has ended but we may still block for up
// to 256 milliseconds after the fact.
//
@@ -36,7 +37,7 @@ int WaitpidWithTimeout(ProcessHandle handle,
// affect other parts of the application and would be difficult to debug.
//
// Our strategy is to call waitpid() once up front to check if the process
- // has already exited, otherwise to loop for wait_milliseconds, sleeping for
+ // has already exited, otherwise to loop for |wait|, sleeping for
// at most 256 milliseconds each time using usleep() and then calling
// waitpid(). The amount of time we sleep starts out at 1 milliseconds, and
// we double it every 4 sleep cycles.
@@ -47,15 +48,18 @@ int WaitpidWithTimeout(ProcessHandle handle,
//
// This function is used primarily for unit tests, if we want to use it in
// the application itself it would probably be best to examine other routes.
- int status = -1;
- pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+
+ if (wait.InMilliseconds() == base::kNoTimeout) {
+ return HANDLE_EINTR(waitpid(handle, status, 0)) > 0;
+ }
+
+ pid_t ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
static const int64 kMaxSleepInMicroseconds = 1 << 18; // ~256 milliseconds.
int64 max_sleep_time_usecs = 1 << 10; // ~1 milliseconds.
int64 double_sleep_time = 0;
// If the process hasn't exited yet, then sleep and try again.
- TimeTicks wakeup_time = TimeTicks::Now() +
- TimeDelta::FromMilliseconds(wait_milliseconds);
+ TimeTicks wakeup_time = TimeTicks::Now() + wait;
while (ret_pid == 0) {
TimeTicks now = TimeTicks::Now();
if (now > wakeup_time)
@@ -69,7 +73,7 @@ int WaitpidWithTimeout(ProcessHandle handle,
// usleep() will return 0 and set errno to EINTR on receipt of a signal
// such as SIGCHLD.
usleep(sleep_time_usecs);
- ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG));
+ ret_pid = HANDLE_EINTR(waitpid(handle, status, WNOHANG));
if ((max_sleep_time_usecs < kMaxSleepInMicroseconds) &&
(double_sleep_time++ % 4 == 0)) {
@@ -77,10 +81,7 @@ int WaitpidWithTimeout(ProcessHandle handle,
}
}
- if (success)
- *success = (ret_pid != -1);
-
- return status;
+ return ret_pid > 0;
}
TerminationStatus GetTerminationStatusImpl(ProcessHandle handle,
@@ -225,12 +226,8 @@ bool WaitForExitCode(ProcessHandle handle, int* exit_code) {
bool WaitForExitCodeWithTimeout(ProcessHandle handle,
int* exit_code,
base::TimeDelta timeout) {
- bool waitpid_success = false;
- int status = WaitpidWithTimeout(handle, timeout.InMilliseconds(),
- &waitpid_success);
- if (status == -1)
- return false;
- if (!waitpid_success)
+ int status;
+ if (!WaitpidWithTimeout(handle, &status, timeout))
return false;
if (WIFSIGNALED(status)) {
*exit_code = -1;
@@ -273,16 +270,15 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle,
DCHECK_GT(handle, 0);
DCHECK(wait.InMilliseconds() == base::kNoTimeout || wait > base::TimeDelta());
- int kq = kqueue();
- if (kq == -1) {
+ ScopedFD kq(kqueue());
+ if (!kq.is_valid()) {
DPLOG(ERROR) << "kqueue";
return false;
}
- file_util::ScopedFD kq_closer(&kq);
struct kevent change = {0};
EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
- int result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
+ int result = HANDLE_EINTR(kevent(kq.get(), &change, 1, NULL, 0, NULL));
if (result == -1) {
if (errno == ESRCH) {
// If the process wasn't found, it must be dead.
@@ -316,7 +312,7 @@ static bool WaitForSingleNonChildProcess(ProcessHandle handle,
remaining_timespec_ptr = &remaining_timespec;
}
- result = kevent(kq, NULL, 0, &event, 1, remaining_timespec_ptr);
+ result = kevent(kq.get(), NULL, 0, &event, 1, remaining_timespec_ptr);
if (result == -1 && errno == EINTR) {
if (!wait_forever) {
@@ -369,21 +365,10 @@ bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) {
#endif // OS_MACOSX
}
- bool waitpid_success;
- int status = -1;
- if (wait.InMilliseconds() == base::kNoTimeout) {
- waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1);
- } else {
- status = WaitpidWithTimeout(
- handle, wait.InMilliseconds(), &waitpid_success);
- }
-
- if (status != -1) {
- DCHECK(waitpid_success);
- return WIFEXITED(status);
- } else {
+ int status;
+ if (!WaitpidWithTimeout(handle, &status, wait))
return false;
- }
+ return WIFEXITED(status);
}
bool CleanupProcesses(const FilePath::StringType& executable_name,
diff --git a/chromium/base/process/kill_win.cc b/chromium/base/process/kill_win.cc
index 99a7c661851..aaf3f30a936 100644
--- a/chromium/base/process/kill_win.cc
+++ b/chromium/base/process/kill_win.cc
@@ -96,9 +96,9 @@ bool KillProcess(ProcessHandle process, int exit_code, bool wait) {
if (result && wait) {
// The process may not end immediately due to pending I/O
if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
- DLOG_GETLASTERROR(ERROR) << "Error waiting for process exit";
+ DPLOG(ERROR) << "Error waiting for process exit";
} else if (!result) {
- DLOG_GETLASTERROR(ERROR) << "Unable to terminate process";
+ DPLOG(ERROR) << "Unable to terminate process";
}
return result;
}
@@ -111,7 +111,7 @@ bool KillProcessById(ProcessId process_id, int exit_code, bool wait) {
FALSE, // Don't inherit handle
process_id);
if (!process) {
- DLOG_GETLASTERROR(ERROR) << "Unable to open process " << process_id;
+ DPLOG(ERROR) << "Unable to open process " << process_id;
return false;
}
bool ret = KillProcess(process, exit_code, wait);
@@ -123,7 +123,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
DWORD tmp_exit_code = 0;
if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
- DLOG_GETLASTERROR(FATAL) << "GetExitCodeProcess() failed";
+ DPLOG(FATAL) << "GetExitCodeProcess() failed";
if (exit_code) {
// This really is a random number. We haven't received any
// information about the exit code, presumably because this
@@ -149,7 +149,7 @@ TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
}
if (wait_result == WAIT_FAILED) {
- DLOG_GETLASTERROR(ERROR) << "WaitForSingleObject() failed";
+ DPLOG(ERROR) << "WaitForSingleObject() failed";
} else {
DCHECK_EQ(WAIT_OBJECT_0, wait_result);
diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc
index 0c9f3a88fef..a1c4d2159cb 100644
--- a/chromium/base/process/launch.cc
+++ b/chromium/base/process/launch.cc
@@ -20,11 +20,13 @@ LaunchOptions::LaunchOptions()
stderr_handle(NULL),
force_breakaway_from_job_(false)
#else
+ clear_environ(false),
fds_to_remap(NULL),
maximize_rlimits(NULL),
new_process_group(false)
#if defined(OS_LINUX)
, clone_flags(0)
+ , allow_new_privs(false)
#endif // OS_LINUX
#if defined(OS_CHROMEOS)
, ctrl_terminal_fd(-1)
@@ -36,4 +38,15 @@ LaunchOptions::LaunchOptions()
LaunchOptions::~LaunchOptions() {
}
+LaunchOptions LaunchOptionsForTest() {
+ LaunchOptions options;
+#if defined(OS_LINUX)
+ // To prevent accidental privilege sharing to an untrusted child, processes
+ // are started with PR_SET_NO_NEW_PRIVS. Do not set that here, since this
+ // new child will be used for testing only.
+ options.allow_new_privs = true;
+#endif
+ return options;
+}
+
} // namespace base
diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h
index 336bfba16e9..261019b138f 100644
--- a/chromium/base/process/launch.h
+++ b/chromium/base/process/launch.h
@@ -7,7 +7,6 @@
#ifndef BASE_PROCESS_LAUNCH_H_
#define BASE_PROCESS_LAUNCH_H_
-#include <set>
#include <string>
#include <utility>
#include <vector>
@@ -25,10 +24,10 @@
#include "base/win/scoped_handle.h"
#endif
-class CommandLine;
-
namespace base {
+class CommandLine;
+
#if defined(OS_WIN)
typedef std::vector<HANDLE> HandlesToInheritVector;
#endif
@@ -89,10 +88,15 @@ struct BASE_EXPORT LaunchOptions {
// job if any.
bool force_breakaway_from_job_;
#else
- // Set/unset environment variables. Empty (the default) means to inherit
- // the same environment. See AlterEnvironment().
+ // Set/unset environment variables. These are applied on top of the parent
+ // process environment. Empty (the default) means to inherit the same
+ // environment. See AlterEnvironment().
EnvironmentMap environ;
+ // Clear the environment for the new process before processing changes from
+ // |environ|.
+ bool clear_environ;
+
// If non-null, remap file descriptors according to the mapping of
// src fd->dest fd to propagate FDs into the child process.
// This pointer is owned by the caller and must live through the
@@ -102,7 +106,7 @@ struct BASE_EXPORT LaunchOptions {
// Each element is an RLIMIT_* constant that should be raised to its
// rlim_max. This pointer is owned by the caller and must live through
// the call to LaunchProcess().
- const std::set<int>* maximize_rlimits;
+ const std::vector<int>* maximize_rlimits;
// If true, start the process in a new process group, instead of
// inheriting the parent's process group. The pgid of the child process
@@ -112,6 +116,10 @@ struct BASE_EXPORT LaunchOptions {
#if defined(OS_LINUX)
// If non-zero, start the process using clone(), using flags as provided.
int clone_flags;
+
+ // By default, child processes will have the PR_SET_NO_NEW_PRIVS bit set. If
+ // true, then this bit will not be set in the new child process.
+ bool allow_new_privs;
#endif // defined(OS_LINUX)
#if defined(OS_CHROMEOS)
@@ -120,6 +128,15 @@ struct BASE_EXPORT LaunchOptions {
int ctrl_terminal_fd;
#endif // defined(OS_CHROMEOS)
+#if defined(OS_MACOSX)
+ // If this name is non-empty, the new child, after fork() but before exec(),
+ // will look up this server name in the bootstrap namespace. The resulting
+ // service port will be replaced as the bootstrap port in the child. Because
+ // the process's IPC space is cleared on exec(), any rights to the old
+ // bootstrap port will not be transferred to the new process.
+ std::string replacement_bootstrap_name;
+#endif
+
#endif // !defined(OS_WIN)
};
@@ -160,6 +177,16 @@ BASE_EXPORT bool LaunchProcess(const string16& cmdline,
const LaunchOptions& options,
win::ScopedHandle* process_handle);
+// Launches a process with elevated privileges. This does not behave exactly
+// like LaunchProcess as it uses ShellExecuteEx instead of CreateProcess to
+// create the process. This means the process will have elevated privileges
+// and thus some common operations like OpenProcess will fail. The process will
+// be available through the |process_handle| argument. Currently the only
+// supported LaunchOptions are |start_hidden| and |wait|.
+BASE_EXPORT bool LaunchElevatedProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle);
+
#elif defined(OS_POSIX)
// A POSIX-specific version of LaunchProcess that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
@@ -232,8 +259,17 @@ BASE_EXPORT void RaiseProcessToHighPriority();
// in the child after forking will restore the standard exception handler.
// See http://crbug.com/20371/ for more details.
void RestoreDefaultExceptionHandler();
+
+// Look up the bootstrap server named |replacement_bootstrap_name| via the
+// current |bootstrap_port|. Then replace the task's bootstrap port with the
+// received right.
+void ReplaceBootstrapPort(const std::string& replacement_bootstrap_name);
#endif // defined(OS_MACOSX)
+// Creates a LaunchOptions object suitable for launching processes in a test
+// binary. This should not be called in production/released code.
+BASE_EXPORT LaunchOptions LaunchOptionsForTest();
+
} // namespace base
#endif // BASE_PROCESS_LAUNCH_H_
diff --git a/chromium/base/process/launch_mac.cc b/chromium/base/process/launch_mac.cc
index 176edca72ea..ce02475541e 100644
--- a/chromium/base/process/launch_mac.cc
+++ b/chromium/base/process/launch_mac.cc
@@ -5,6 +5,9 @@
#include "base/process/launch.h"
#include <mach/mach.h>
+#include <servers/bootstrap.h>
+
+#include "base/logging.h"
namespace base {
@@ -25,4 +28,21 @@ void RestoreDefaultExceptionHandler() {
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
}
+void ReplaceBootstrapPort(const std::string& new_bootstrap_name) {
+ // This function is called between fork() and exec(), so it should take care
+ // to run properly in that situation.
+
+ mach_port_t port = MACH_PORT_NULL;
+ kern_return_t kr = bootstrap_look_up(bootstrap_port,
+ new_bootstrap_name.c_str(), &port);
+ if (kr != KERN_SUCCESS) {
+ RAW_LOG(FATAL, "Failed to look up replacement bootstrap port.");
+ }
+
+ kr = task_set_bootstrap_port(mach_task_self(), port);
+ if (kr != KERN_SUCCESS) {
+ RAW_LOG(FATAL, "Failed to replace bootstrap port.");
+ }
+}
+
} // namespace base
diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc
index 8dc8f9e215d..78a3be47eb0 100644
--- a/chromium/base/process/launch_posix.cc
+++ b/chromium/base/process/launch_posix.cc
@@ -26,6 +26,7 @@
#include "base/debug/stack_trace.h"
#include "base/file_util.h"
#include "base/files/dir_reader_posix.h"
+#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/posix/eintr_wrapper.h"
@@ -37,6 +38,10 @@
#include "base/threading/platform_thread.h"
#include "base/threading/thread_restrictions.h"
+#if defined(OS_LINUX)
+#include <sys/prctl.h>
+#endif
+
#if defined(OS_CHROMEOS)
#include <sys/ioctl.h>
#endif
@@ -181,16 +186,16 @@ void ResetChildSignalHandlersToDefaults(void) {
} // anonymous namespace
-// A class to handle auto-closing of DIR*'s.
-class ScopedDIRClose {
- public:
+// Functor for |ScopedDIR| (below).
+struct ScopedDIRClose {
inline void operator()(DIR* x) const {
- if (x) {
+ if (x)
closedir(x);
- }
}
};
-typedef scoped_ptr_malloc<DIR, ScopedDIRClose> ScopedDIR;
+
+// Automatically closes |DIR*|s.
+typedef scoped_ptr<DIR, ScopedDIRClose> ScopedDIR;
#if defined(OS_LINUX)
static const char kFDDir[] = "/proc/self/fd";
@@ -207,7 +212,7 @@ static const char kFDDir[] = "/proc/self/fd";
#endif
void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
- // DANGER: no calls to malloc are allowed from now on:
+ // DANGER: no calls to malloc or locks are allowed from now on:
// http://crbug.com/36678
// Get the maximum number of FDs possible.
@@ -220,12 +225,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
const int fd = static_cast<int>(i);
if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
continue;
- InjectiveMultimap::const_iterator j;
- for (j = saved_mapping.begin(); j != saved_mapping.end(); j++) {
- if (fd == j->dest)
+ // Cannot use STL iterators here, since debug iterators use locks.
+ size_t j;
+ for (j = 0; j < saved_mapping.size(); j++) {
+ if (fd == saved_mapping[j].dest)
break;
}
- if (j != saved_mapping.end())
+ if (j < saved_mapping.size())
continue;
// Since we're just trying to close anything we can find,
@@ -249,12 +255,13 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
continue;
if (fd == STDIN_FILENO || fd == STDOUT_FILENO || fd == STDERR_FILENO)
continue;
- InjectiveMultimap::const_iterator i;
- for (i = saved_mapping.begin(); i != saved_mapping.end(); i++) {
- if (fd == i->dest)
+ // Cannot use STL iterators here, since debug iterators use locks.
+ size_t i;
+ for (i = 0; i < saved_mapping.size(); i++) {
+ if (fd == saved_mapping[i].dest)
break;
}
- if (i != saved_mapping.end())
+ if (i < saved_mapping.size())
continue;
if (fd == dir_fd)
continue;
@@ -285,8 +292,12 @@ bool LaunchProcess(const std::vector<std::string>& argv,
scoped_ptr<char*[]> argv_cstr(new char*[argv.size() + 1]);
scoped_ptr<char*[]> new_environ;
+ char* const empty_environ = NULL;
+ char* const* old_environ = GetEnvironment();
+ if (options.clear_environ)
+ old_environ = &empty_environ;
if (!options.environ.empty())
- new_environ = AlterEnvironment(GetEnvironment(), options.environ);
+ new_environ = AlterEnvironment(old_environ, options.environ);
sigset_t full_sigset;
sigfillset(&full_sigset);
@@ -318,6 +329,9 @@ bool LaunchProcess(const std::vector<std::string>& argv,
} else if (pid == 0) {
// Child process
+ // DANGER: no calls to malloc or locks are allowed from now on:
+ // http://crbug.com/36678
+
// DANGER: fork() rule: in the child, if you don't end up doing exec*(),
// you call _exit() instead of exit(). This is because _exit() does not
// call any previously-registered (in the parent) exit handlers, which
@@ -327,14 +341,13 @@ bool LaunchProcess(const std::vector<std::string>& argv,
// If a child process uses the readline library, the process block forever.
// In BSD like OSes including OS X it is safe to assign /dev/null as stdin.
// See http://crbug.com/56596.
- int null_fd = HANDLE_EINTR(open("/dev/null", O_RDONLY));
- if (null_fd < 0) {
+ base::ScopedFD null_fd(HANDLE_EINTR(open("/dev/null", O_RDONLY)));
+ if (!null_fd.is_valid()) {
RAW_LOG(ERROR, "Failed to open /dev/null");
_exit(127);
}
- file_util::ScopedFD null_fd_closer(&null_fd);
- int new_fd = HANDLE_EINTR(dup2(null_fd, STDIN_FILENO));
+ int new_fd = HANDLE_EINTR(dup2(null_fd.get(), STDIN_FILENO));
if (new_fd != STDIN_FILENO) {
RAW_LOG(ERROR, "Failed to dup /dev/null for stdin");
_exit(127);
@@ -356,16 +369,14 @@ bool LaunchProcess(const std::vector<std::string>& argv,
if (options.maximize_rlimits) {
// Some resource limits need to be maximal in this child.
- std::set<int>::const_iterator resource;
- for (resource = options.maximize_rlimits->begin();
- resource != options.maximize_rlimits->end();
- ++resource) {
+ for (size_t i = 0; i < options.maximize_rlimits->size(); ++i) {
+ const int resource = (*options.maximize_rlimits)[i];
struct rlimit limit;
- if (getrlimit(*resource, &limit) < 0) {
+ if (getrlimit(resource, &limit) < 0) {
RAW_LOG(WARNING, "getrlimit failed");
} else if (limit.rlim_cur < limit.rlim_max) {
limit.rlim_cur = limit.rlim_max;
- if (setrlimit(*resource, &limit) < 0) {
+ if (setrlimit(resource, &limit) < 0) {
RAW_LOG(WARNING, "setrlimit failed");
}
}
@@ -374,6 +385,8 @@ bool LaunchProcess(const std::vector<std::string>& argv,
#if defined(OS_MACOSX)
RestoreDefaultExceptionHandler();
+ if (!options.replacement_bootstrap_name.empty())
+ ReplaceBootstrapPort(options.replacement_bootstrap_name);
#endif // defined(OS_MACOSX)
ResetChildSignalHandlersToDefaults();
@@ -388,9 +401,6 @@ bool LaunchProcess(const std::vector<std::string>& argv,
memset(reinterpret_cast<void*>(malloc), 0xff, 8);
#endif // 0
- // DANGER: no calls to malloc are allowed from now on:
- // http://crbug.com/36678
-
#if defined(OS_CHROMEOS)
if (options.ctrl_terminal_fd >= 0) {
// Set process' controlling terminal.
@@ -406,15 +416,16 @@ bool LaunchProcess(const std::vector<std::string>& argv,
#endif // defined(OS_CHROMEOS)
if (options.fds_to_remap) {
- for (FileHandleMappingVector::const_iterator
- it = options.fds_to_remap->begin();
- it != options.fds_to_remap->end(); ++it) {
- fd_shuffle1.push_back(InjectionArc(it->first, it->second, false));
- fd_shuffle2.push_back(InjectionArc(it->first, it->second, false));
+ // Cannot use STL iterators here, since debug iterators use locks.
+ for (size_t i = 0; i < options.fds_to_remap->size(); ++i) {
+ const FileHandleMappingVector::value_type& value =
+ (*options.fds_to_remap)[i];
+ fd_shuffle1.push_back(InjectionArc(value.first, value.second, false));
+ fd_shuffle2.push_back(InjectionArc(value.first, value.second, false));
}
}
- if (!options.environ.empty())
+ if (!options.environ.empty() || options.clear_environ)
SetEnvironment(new_environ.get());
// fd_shuffle1 is mutated by this call because it cannot malloc.
@@ -423,6 +434,20 @@ bool LaunchProcess(const std::vector<std::string>& argv,
CloseSuperfluousFds(fd_shuffle2);
+ // Set NO_NEW_PRIVS by default. Since NO_NEW_PRIVS only exists in kernel
+ // 3.5+, do not check the return value of prctl here.
+#if defined(OS_LINUX)
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#endif
+ if (!options.allow_new_privs) {
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) && errno != EINVAL) {
+ // Only log if the error is not EINVAL (i.e. not supported).
+ RAW_LOG(FATAL, "prctl(PR_SET_NO_NEW_PRIVS) failed");
+ }
+ }
+#endif
+
for (size_t i = 0; i < argv.size(); i++)
argv_cstr[i] = const_cast<char*>(argv[i].c_str());
argv_cstr[argv.size()] = NULL;
@@ -518,11 +543,12 @@ static GetAppOutputInternalResult GetAppOutputInternal(
return EXECUTE_FAILURE;
case 0: // child
{
+ // DANGER: no calls to malloc or locks are allowed from now on:
+ // http://crbug.com/36678
+
#if defined(OS_MACOSX)
RestoreDefaultExceptionHandler();
#endif
- // DANGER: no calls to malloc are allowed from now on:
- // http://crbug.com/36678
// Obscure fork() rule: in the child, if you don't end up doing exec*(),
// you call _exit() instead of exit(). This is because _exit() does not
@@ -544,8 +570,8 @@ static GetAppOutputInternalResult GetAppOutputInternal(
// Adding another element here? Remeber to increase the argument to
// reserve(), above.
- std::copy(fd_shuffle1.begin(), fd_shuffle1.end(),
- std::back_inserter(fd_shuffle2));
+ for (size_t i = 0; i < fd_shuffle1.size(); ++i)
+ fd_shuffle2.push_back(fd_shuffle1[i]);
if (!ShuffleFileDescriptors(&fd_shuffle1))
_exit(127);
diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc
index 0c831cf7b4a..9e4db384ebb 100644
--- a/chromium/base/process/launch_win.cc
+++ b/chromium/base/process/launch_win.cc
@@ -6,6 +6,7 @@
#include <fcntl.h>
#include <io.h>
+#include <shellapi.h>
#include <windows.h>
#include <userenv.h>
#include <psapi.h>
@@ -22,6 +23,7 @@
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/process/kill.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/sys_info.h"
#include "base/win/object_watcher.h"
#include "base/win/scoped_handle.h"
@@ -192,7 +194,8 @@ bool LaunchProcess(const string16& cmdline,
&temp_process_info);
DestroyEnvironmentBlock(enviroment_block);
if (!launched) {
- DPLOG(ERROR);
+ DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
+ << std::endl;;
return false;
}
} else {
@@ -200,7 +203,8 @@ bool LaunchProcess(const string16& cmdline,
const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
inherit_handles, flags, NULL, NULL,
startup_info, &temp_process_info)) {
- DPLOG(ERROR);
+ DPLOG(ERROR) << "Command line:" << std::endl << UTF16ToUTF8(cmdline)
+ << std::endl;;
return false;
}
}
@@ -239,6 +243,41 @@ bool LaunchProcess(const CommandLine& cmdline,
return rv;
}
+bool LaunchElevatedProcess(const CommandLine& cmdline,
+ const LaunchOptions& options,
+ ProcessHandle* process_handle) {
+ const string16 file = cmdline.GetProgram().value();
+ const string16 arguments = cmdline.GetArgumentsString();
+
+ SHELLEXECUTEINFO shex_info = {0};
+ shex_info.cbSize = sizeof(shex_info);
+ shex_info.fMask = SEE_MASK_NOCLOSEPROCESS;
+ shex_info.hwnd = GetActiveWindow();
+ shex_info.lpVerb = L"runas";
+ shex_info.lpFile = file.c_str();
+ shex_info.lpParameters = arguments.c_str();
+ shex_info.lpDirectory = NULL;
+ shex_info.nShow = options.start_hidden ? SW_HIDE : SW_SHOW;
+ shex_info.hInstApp = NULL;
+
+ if (!ShellExecuteEx(&shex_info)) {
+ DPLOG(ERROR);
+ return false;
+ }
+
+ if (options.wait)
+ WaitForSingleObject(shex_info.hProcess, INFINITE);
+
+ // If the caller wants the process handle give it to them, otherwise just
+ // close it. Closing it does not terminate the process.
+ if (process_handle)
+ *process_handle = shex_info.hProcess;
+ else
+ CloseHandle(shex_info.hProcess);
+
+ return true;
+}
+
bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info = {0};
limit_info.BasicLimitInformation.LimitFlags = limit_flags;
diff --git a/chromium/base/process/memory.cc b/chromium/base/process/memory.cc
new file mode 100644
index 00000000000..1dbc3630703
--- /dev/null
+++ b/chromium/base/process/memory.cc
@@ -0,0 +1,30 @@
+// Copyright 2014 The Chromium Authors. All 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/memory.h"
+
+namespace base {
+
+// Defined in memory_mac.mm for Mac.
+#if !defined(OS_MACOSX)
+
+bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
+ const size_t alloc_size = num_items * size;
+
+ // Overflow check
+ if (size && ((alloc_size / size) != num_items)) {
+ *result = NULL;
+ return false;
+ }
+
+ if (!UncheckedMalloc(alloc_size, result))
+ return false;
+
+ memset(*result, 0, alloc_size);
+ return true;
+}
+
+#endif
+
+}
diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h
index e6696cb8a70..100d9c72f43 100644
--- a/chromium/base/process/memory.h
+++ b/chromium/base/process/memory.h
@@ -14,6 +14,14 @@
#include <windows.h>
#endif
+#ifdef PVALLOC_AVAILABLE
+// Build config explicitly tells us whether or not pvalloc is available.
+#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+#define PVALLOC_AVAILABLE 1
+#else
+#define PVALLOC_AVAILABLE 0
+#endif
+
namespace base {
// Enables low fragmentation heap (LFH) for every heaps of this process. This
@@ -53,17 +61,21 @@ const int kMaxOomScore = 1000;
BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
#endif
-#if defined(OS_MACOSX)
-// Very large images or svg canvases can cause huge mallocs. Skia
-// does tricks on tcmalloc-based systems to allow malloc to fail with
-// a NULL rather than hit the oom crasher. This replicates that for
-// OSX.
-//
-// IF YOU USE THIS WITHOUT CONSULTING YOUR FRIENDLY OSX DEVELOPER,
-// YOUR CODE IS LIKELY TO BE REVERTED. THANK YOU.
-BASE_EXPORT void* UncheckedMalloc(size_t size);
-BASE_EXPORT void* UncheckedCalloc(size_t num_items, size_t size);
-#endif // defined(OS_MACOSX)
+// Special allocator functions for callers that want to check for OOM.
+// These will not abort if the allocation fails even if
+// EnableTerminationOnOutOfMemory has been called.
+// This can be useful for huge and/or unpredictable size memory allocations.
+// Please only use this if you really handle the case when the allocation
+// fails. Doing otherwise would risk security.
+// These functions may still crash on OOM when running under memory tools,
+// specifically ASan and other sanitizers.
+// Return value tells whether the allocation succeeded. If it fails |result| is
+// set to NULL, otherwise it holds the memory address.
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedMalloc(size_t size,
+ void** result);
+BASE_EXPORT WARN_UNUSED_RESULT bool UncheckedCalloc(size_t num_items,
+ size_t size,
+ void** result);
} // namespace base
diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc
index 6bed68bd832..befd832d667 100644
--- a/chromium/base/process/memory_linux.cc
+++ b/chromium/base/process/memory_linux.cc
@@ -12,6 +12,22 @@
#include "base/process/internal_linux.h"
#include "base/strings/string_number_conversions.h"
+#if defined(USE_TCMALLOC)
+// Used by UncheckedMalloc. If tcmalloc is linked to the executable
+// this will be replaced by a strong symbol that actually implement
+// the semantics and don't call new handler in case the allocation fails.
+extern "C" {
+
+__attribute__((weak, visibility("default")))
+void* tc_malloc_skip_new_handler_weak(size_t size);
+
+void* tc_malloc_skip_new_handler_weak(size_t size) {
+ return malloc(size);
+}
+
+}
+#endif
+
namespace base {
size_t g_oom_size = 0U;
@@ -44,7 +60,9 @@ void* __libc_malloc(size_t size);
void* __libc_realloc(void* ptr, size_t size);
void* __libc_calloc(size_t nmemb, size_t size);
void* __libc_valloc(size_t size);
+#if PVALLOC_AVAILABLE == 1
void* __libc_pvalloc(size_t size);
+#endif
void* __libc_memalign(size_t alignment, size_t size);
// Overriding the system memory allocation functions:
@@ -99,7 +117,9 @@ void* __libc_memalign(size_t alignment, size_t size);
DIE_ON_OOM_1(malloc)
DIE_ON_OOM_1(valloc)
+#if PVALLOC_AVAILABLE == 1
DIE_ON_OOM_1(pvalloc)
+#endif
DIE_ON_OOM_2(calloc, size_t)
DIE_ON_OOM_2(realloc, void*)
@@ -157,9 +177,7 @@ bool AdjustOOMScore(ProcessId process, int score) {
DVLOG(1) << "Adjusting oom_score_adj of " << process << " to "
<< score_str;
int score_len = static_cast<int>(score_str.length());
- return (score_len == file_util::WriteFile(oom_file,
- score_str.c_str(),
- score_len));
+ return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
}
// If the oom_score_adj file doesn't exist, then we write the old
@@ -174,12 +192,22 @@ bool AdjustOOMScore(ProcessId process, int score) {
std::string score_str = IntToString(converted_score);
DVLOG(1) << "Adjusting oom_adj of " << process << " to " << score_str;
int score_len = static_cast<int>(score_str.length());
- return (score_len == file_util::WriteFile(oom_file,
- score_str.c_str(),
- score_len));
+ return (score_len == WriteFile(oom_file, score_str.c_str(), score_len));
}
return false;
}
+bool UncheckedMalloc(size_t size, void** result) {
+#if defined(MEMORY_TOOL_REPLACES_ALLOCATOR) || \
+ (!defined(LIBC_GLIBC) && !defined(USE_TCMALLOC))
+ *result = malloc(size);
+#elif defined(LIBC_GLIBC) && !defined(USE_TCMALLOC)
+ *result = __libc_malloc(size);
+#elif defined(USE_TCMALLOC)
+ *result = tc_malloc_skip_new_handler_weak(size);
+#endif
+ return *result != NULL;
+}
+
} // namespace base
diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm
index 3e281cd8e3c..575e886a175 100644
--- a/chromium/base/process/memory_mac.mm
+++ b/chromium/base/process/memory_mac.mm
@@ -16,6 +16,7 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/mac/mac_util.h"
+#include "base/mac/mach_logging.h"
#include "base/scoped_clear_errno.h"
#include "third_party/apple_apsl/CFBase.h"
#include "third_party/apple_apsl/malloc.h"
@@ -222,10 +223,12 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone,
reinterpret_cast<vm_region_info_t>(&info),
&count,
&unused);
- CHECK(result == KERN_SUCCESS);
+ MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_region";
- result = mach_port_deallocate(mach_task_self(), unused);
- CHECK(result == KERN_SUCCESS);
+ // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but
+ // balance it with a deallocate in case this ever changes. See 10.9.2
+ // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region.
+ mach_port_deallocate(mach_task_self(), unused);
// Does the region fully enclose the zone pointers? Possibly unwarranted
// simplification used: using the size of a full version 8 malloc zone rather
@@ -248,7 +251,7 @@ void DeprotectMallocZone(ChromeMallocZone* default_zone,
*reprotection_length,
false,
info.protection | VM_PROT_WRITE);
- CHECK(result == KERN_SUCCESS);
+ MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect";
}
}
@@ -435,7 +438,7 @@ void oom_killer_new() {
// === Core Foundation CFAllocators ===
bool CanGetContextForCFAllocator() {
- return !base::mac::IsOSLaterThanMavericks_DontCallThis();
+ return !base::mac::IsOSLaterThanYosemite_DontCallThis();
}
CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
@@ -446,7 +449,8 @@ CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
return &our_allocator->_context;
} else if (base::mac::IsOSLion() ||
base::mac::IsOSMountainLion() ||
- base::mac::IsOSMavericks()) {
+ base::mac::IsOSMavericks() ||
+ base::mac::IsOSYosemite()) {
ChromeCFAllocatorLions* our_allocator =
const_cast<ChromeCFAllocatorLions*>(
reinterpret_cast<const ChromeCFAllocatorLions*>(allocator));
@@ -502,26 +506,42 @@ id oom_killer_allocWithZone(id self, SEL _cmd, NSZone* zone)
} // namespace
-void* UncheckedMalloc(size_t size) {
+bool UncheckedMalloc(size_t size, void** result) {
if (g_old_malloc) {
#if ARCH_CPU_32_BITS
ScopedClearErrno clear_errno;
ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
#endif // ARCH_CPU_32_BITS
- return g_old_malloc(malloc_default_zone(), size);
+ *result = g_old_malloc(malloc_default_zone(), size);
+ } else {
+ *result = malloc(size);
}
- return malloc(size);
+
+ return *result != NULL;
}
-void* UncheckedCalloc(size_t num_items, size_t size) {
+bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
if (g_old_calloc) {
#if ARCH_CPU_32_BITS
ScopedClearErrno clear_errno;
ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
#endif // ARCH_CPU_32_BITS
- return g_old_calloc(malloc_default_zone(), num_items, size);
+ *result = g_old_calloc(malloc_default_zone(), num_items, size);
+ } else {
+ *result = calloc(num_items, size);
}
- return calloc(num_items, size);
+
+ return *result != NULL;
+}
+
+void* UncheckedMalloc(size_t size) {
+ void* address;
+ return UncheckedMalloc(size, &address) ? address : NULL;
+}
+
+void* UncheckedCalloc(size_t num_items, size_t size) {
+ void* address;
+ return UncheckedCalloc(num_items, size, &address) ? address : NULL;
}
void EnableTerminationOnOutOfMemory() {
@@ -630,7 +650,7 @@ void EnableTerminationOnOutOfMemory() {
default_reprotection_length,
false,
default_reprotection_value);
- CHECK(result == KERN_SUCCESS);
+ MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect";
}
if (purgeable_reprotection_start) {
@@ -639,7 +659,7 @@ void EnableTerminationOnOutOfMemory() {
purgeable_reprotection_length,
false,
purgeable_reprotection_value);
- CHECK(result == KERN_SUCCESS);
+ MACH_CHECK(result == KERN_SUCCESS, result) << "mach_vm_protect";
}
#endif
@@ -703,8 +723,9 @@ void EnableTerminationOnOutOfMemory() {
<< "Failed to get kCFAllocatorMallocZone allocation function.";
context->allocate = oom_killer_cfallocator_malloc_zone;
} else {
- NSLog(@"Internals of CFAllocator not known; out-of-memory failures via "
- "CFAllocator will not result in termination. http://crbug.com/45650");
+ DLOG(WARNING) << "Internals of CFAllocator not known; out-of-memory "
+ "failures via CFAllocator will not result in termination. "
+ "http://crbug.com/45650";
}
#endif
diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc
index e5c759d5711..048c09d38c1 100644
--- a/chromium/base/process/memory_unittest.cc
+++ b/chromium/base/process/memory_unittest.cc
@@ -121,9 +121,7 @@ TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) {
buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1);
},
testing::KilledBySignal(SIGTRAP),
- "\\*\\*\\* error: can't allocate region.*"
- "(Terminating process due to a potential for future heap "
- "corruption){0}");
+ "\\*\\*\\* error: can't allocate region.*\\n?.*");
base::debug::Alias(buf);
}
@@ -143,8 +141,8 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
ASSERT_DEATH(free(buf), "attempting free on address which "
"was not malloc\\(\\)-ed");
#else
- ASSERT_DEATH(free(buf), "being freed.*"
- "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*"
+ ASSERT_DEATH(free(buf), "being freed.*\\n?\\.*"
+ "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*\\n?.*"
"Terminating process due to a potential for future heap corruption");
#endif // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER)
}
@@ -154,13 +152,9 @@ TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
// Android doesn't implement set_new_handler, so we can't use the
// OutOfMemoryTest cases.
// OpenBSD does not support these tests either.
-// AddressSanitizer and ThreadSanitizer define the malloc()/free()/etc.
-// functions so that they don't crash if the program is out of memory, so the
-// OOM tests aren't supposed to work.
// TODO(vandebo) make this work on Windows too.
#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \
- !defined(OS_WIN) && \
- !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER)
+ !defined(OS_WIN)
#if defined(USE_TCMALLOC)
extern "C" {
@@ -168,14 +162,14 @@ int tc_set_new_mode(int mode);
}
#endif // defined(USE_TCMALLOC)
-class OutOfMemoryDeathTest : public testing::Test {
+class OutOfMemoryTest : public testing::Test {
public:
- OutOfMemoryDeathTest()
- : value_(NULL),
- // Make test size as large as possible minus a few pages so
- // that alignment or other rounding doesn't make it wrap.
- test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
- signed_test_size_(std::numeric_limits<ssize_t>::max()) {
+ OutOfMemoryTest()
+ : value_(NULL),
+ // Make test size as large as possible minus a few pages so
+ // that alignment or other rounding doesn't make it wrap.
+ test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
+ signed_test_size_(std::numeric_limits<ssize_t>::max()) {
}
#if defined(USE_TCMALLOC)
@@ -188,6 +182,14 @@ class OutOfMemoryDeathTest : public testing::Test {
}
#endif // defined(USE_TCMALLOC)
+ protected:
+ void* value_;
+ size_t test_size_;
+ ssize_t signed_test_size_;
+};
+
+class OutOfMemoryDeathTest : public OutOfMemoryTest {
+ public:
void SetUpInDeathAssert() {
// Must call EnableTerminationOnOutOfMemory() because that is called from
// chrome's main function and therefore hasn't been called yet.
@@ -196,10 +198,6 @@ class OutOfMemoryDeathTest : public testing::Test {
// should be done inside of the ASSERT_DEATH.
base::EnableTerminationOnOutOfMemory();
}
-
- void* value_;
- size_t test_size_;
- ssize_t signed_test_size_;
};
TEST_F(OutOfMemoryDeathTest, New) {
@@ -245,12 +243,15 @@ TEST_F(OutOfMemoryDeathTest, Valloc) {
}
#if defined(OS_LINUX)
+
+#if PVALLOC_AVAILABLE == 1
TEST_F(OutOfMemoryDeathTest, Pvalloc) {
ASSERT_DEATH({
SetUpInDeathAssert();
value_ = pvalloc(test_size_);
}, "");
}
+#endif // PVALLOC_AVAILABLE == 1
TEST_F(OutOfMemoryDeathTest, Memalign) {
ASSERT_DEATH({
@@ -374,5 +375,54 @@ TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
#endif // !ARCH_CPU_64_BITS
#endif // OS_MACOSX
-#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) &&
- // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER)
+class OutOfMemoryHandledTest : public OutOfMemoryTest {
+ public:
+ static const size_t kSafeMallocSize = 512;
+ static const size_t kSafeCallocSize = 128;
+ static const size_t kSafeCallocItems = 4;
+
+ virtual void SetUp() {
+ OutOfMemoryTest::SetUp();
+
+ // We enable termination on OOM - just as Chrome does at early
+ // initialization - and test that UncheckedMalloc and UncheckedCalloc
+ // properly by-pass this in order to allow the caller to handle OOM.
+ base::EnableTerminationOnOutOfMemory();
+ }
+};
+
+// TODO(b.kelemen): make UncheckedMalloc and UncheckedCalloc work
+// on Windows as well.
+// UncheckedMalloc() and UncheckedCalloc() work as regular malloc()/calloc()
+// under sanitizer tools.
+#if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) {
+ EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &value_));
+ EXPECT_TRUE(value_ != NULL);
+ free(value_);
+
+ EXPECT_FALSE(base::UncheckedMalloc(test_size_, &value_));
+ EXPECT_TRUE(value_ == NULL);
+}
+
+TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) {
+ EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &value_));
+ EXPECT_TRUE(value_ != NULL);
+ const char* bytes = static_cast<const char*>(value_);
+ for (size_t i = 0; i < kSafeMallocSize; ++i)
+ EXPECT_EQ(0, bytes[i]);
+ free(value_);
+
+ EXPECT_TRUE(
+ base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &value_));
+ EXPECT_TRUE(value_ != NULL);
+ bytes = static_cast<const char*>(value_);
+ for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i)
+ EXPECT_EQ(0, bytes[i]);
+ free(value_);
+
+ EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &value_));
+ EXPECT_TRUE(value_ == NULL);
+}
+#endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
+#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && !defined(OS_WIN)
diff --git a/chromium/base/process/memory_win.cc b/chromium/base/process/memory_win.cc
index c53a1be5395..668214ceaf0 100644
--- a/chromium/base/process/memory_win.cc
+++ b/chromium/base/process/memory_win.cc
@@ -82,4 +82,15 @@ HMODULE GetModuleFromAddress(void* address) {
return instance;
}
+// TODO(b.kelemen): implement it with the required semantics. On Linux this is
+// implemented with a weak symbol that is overridden by tcmalloc. This is
+// neccessary because base cannot have a direct dependency on tcmalloc. Since
+// weak symbols are not supported on Windows this will involve some build time
+// magic, much like what is done for libcrt in order to override the allocation
+// functions.
+bool UncheckedMalloc(size_t size, void** result) {
+ *result = malloc(size);
+ return *result != NULL;
+}
+
} // namespace base
diff --git a/chromium/base/process/process_handle.h b/chromium/base/process/process_handle.h
index a37784275ea..6f8094ee80b 100644
--- a/chromium/base/process/process_handle.h
+++ b/chromium/base/process/process_handle.h
@@ -81,12 +81,10 @@ BASE_EXPORT bool GetProcessIntegrityLevel(ProcessHandle process,
IntegrityLevel* level);
#endif
-#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_BSD)
+#if defined(OS_POSIX)
// Returns the path to the executable of the given process.
BASE_EXPORT FilePath GetProcessExecutablePath(ProcessHandle process);
-#endif
-#if defined(OS_POSIX)
// Returns the ID for the parent of the given process.
BASE_EXPORT ProcessId GetParentProcessId(ProcessHandle process);
#endif
diff --git a/chromium/base/process/process_handle_freebsd.cc b/chromium/base/process/process_handle_freebsd.cc
index 8a0e9de8f2d..e465a85b7e0 100644
--- a/chromium/base/process/process_handle_freebsd.cc
+++ b/chromium/base/process/process_handle_freebsd.cc
@@ -6,6 +6,7 @@
#include <sys/sysctl.h>
#include <sys/types.h>
+#include <sys/user.h>
#include <unistd.h>
namespace base {
diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc
index 0f7ccd348a8..6c5cd2fa182 100644
--- a/chromium/base/process/process_handle_linux.cc
+++ b/chromium/base/process/process_handle_linux.cc
@@ -11,7 +11,7 @@ namespace base {
ProcessId GetParentProcessId(ProcessHandle process) {
ProcessId pid =
- internal::ReadProcStatsAndGetFieldAsInt(process, internal::VM_PPID);
+ internal::ReadProcStatsAndGetFieldAsInt64(process, internal::VM_PPID);
if (pid)
return pid;
return -1;
diff --git a/chromium/base/process/process_handle_mac.cc b/chromium/base/process/process_handle_mac.cc
index 6cb8d686e4d..cbf0bc5c439 100644
--- a/chromium/base/process/process_handle_mac.cc
+++ b/chromium/base/process/process_handle_mac.cc
@@ -4,6 +4,7 @@
#include "base/process/process_handle.h"
+#include <libproc.h>
#include <sys/sysctl.h>
#include <sys/types.h>
@@ -24,4 +25,12 @@ ProcessId GetParentProcessId(ProcessHandle process) {
return info.kp_eproc.e_ppid;
}
+FilePath GetProcessExecutablePath(ProcessHandle process) {
+ char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
+ if (!proc_pidpath(process, pathbuf, sizeof(pathbuf)))
+ return FilePath();
+
+ return FilePath(pathbuf);
+}
+
} // namespace base
diff --git a/chromium/base/process/process_info_linux.cc b/chromium/base/process/process_info_linux.cc
index da34c180e8a..9ec23135bfc 100644
--- a/chromium/base/process/process_info_linux.cc
+++ b/chromium/base/process/process_info_linux.cc
@@ -15,8 +15,8 @@ namespace base {
//static
const Time CurrentProcessInfo::CreationTime() {
ProcessHandle pid = GetCurrentProcessHandle();
- int start_ticks = internal::ReadProcStatsAndGetFieldAsInt(
- pid, internal::VM_STARTTIME);
+ int64 start_ticks =
+ internal::ReadProcStatsAndGetFieldAsInt64(pid, internal::VM_STARTTIME);
DCHECK(start_ticks);
TimeDelta start_offset = internal::ClockTicksToTimeDelta(start_ticks);
Time boot_time = internal::GetBootTime();
diff --git a/chromium/base/process/process_info_mac.cc b/chromium/base/process/process_info_mac.cc
index ab8394b891d..b7cfdceda05 100644
--- a/chromium/base/process/process_info_mac.cc
+++ b/chromium/base/process/process_info_mac.cc
@@ -21,7 +21,7 @@ const Time CurrentProcessInfo::CreationTime() {
if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0)
return Time();
- scoped_ptr_malloc<struct kinfo_proc>
+ scoped_ptr<struct kinfo_proc, base::FreeDeleter>
proc(static_cast<struct kinfo_proc*>(malloc(len)));
if (sysctl(mib, arraysize(mib), proc.get(), &len, NULL, 0) < 0)
return Time();
diff --git a/chromium/base/process/process_iterator.h b/chromium/base/process/process_iterator.h
index aa8ba74aba0..aa2fc4148ad 100644
--- a/chromium/base/process/process_iterator.h
+++ b/chromium/base/process/process_iterator.h
@@ -20,8 +20,10 @@
#if defined(OS_WIN)
#include <windows.h>
#include <tlhelp32.h>
-#elif defined(OS_MACOSX) || defined(OS_BSD)
+#elif defined(OS_MACOSX) || defined(OS_OPENBSD)
#include <sys/sysctl.h>
+#elif defined(OS_FREEBSD)
+#include <sys/user.h>
#elif defined(OS_POSIX)
#include <dirent.h>
#endif
diff --git a/chromium/base/process/process_iterator_freebsd.cc b/chromium/base/process/process_iterator_freebsd.cc
index e8225b0054e..5b1e2ab09db 100644
--- a/chromium/base/process/process_iterator_freebsd.cc
+++ b/chromium/base/process/process_iterator_freebsd.cc
@@ -4,7 +4,9 @@
#include "base/process/process_iterator.h"
+#include <sys/types.h>
#include <sys/sysctl.h>
+#include <unistd.h>
#include "base/logging.h"
#include "base/strings/string_util.h"
diff --git a/chromium/base/process/process_iterator_linux.cc b/chromium/base/process/process_iterator_linux.cc
index 0587d7b88c5..8f746aa3391 100644
--- a/chromium/base/process/process_iterator_linux.cc
+++ b/chromium/base/process/process_iterator_linux.cc
@@ -121,8 +121,8 @@ bool ProcessIterator::CheckForNextProcess() {
}
entry_.pid_ = pid;
- entry_.ppid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PPID);
- entry_.gid_ = GetProcStatsFieldAsInt(proc_stats, internal::VM_PGRP);
+ entry_.ppid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PPID);
+ entry_.gid_ = GetProcStatsFieldAsInt64(proc_stats, internal::VM_PGRP);
entry_.cmd_line_args_.assign(cmd_line_args.begin(), cmd_line_args.end());
entry_.exe_file_ = GetProcessExecutablePath(pid).BaseName().value();
return true;
diff --git a/chromium/base/process/process_iterator_mac.cc b/chromium/base/process/process_iterator_mac.cc
index 29daa2d489f..e35c2ae19ba 100644
--- a/chromium/base/process/process_iterator_mac.cc
+++ b/chromium/base/process/process_iterator_mac.cc
@@ -7,6 +7,7 @@
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/types.h>
+#include <unistd.h>
#include "base/logging.h"
#include "base/strings/string_util.h"
diff --git a/chromium/base/process/process_linux.cc b/chromium/base/process/process_linux.cc
index b12e994848d..d92d7c30eee 100644
--- a/chromium/base/process/process_linux.cc
+++ b/chromium/base/process/process_linux.cc
@@ -14,6 +14,8 @@
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
+namespace base {
+
namespace {
const int kForegroundPriority = 0;
@@ -46,13 +48,13 @@ struct CGroups {
base::FilePath(base::StringPrintf(kControlPath, kForeground));
background_file =
base::FilePath(base::StringPrintf(kControlPath, kBackground));
- file_util::FileSystemType foreground_type;
- file_util::FileSystemType background_type;
+ base::FileSystemType foreground_type;
+ base::FileSystemType background_type;
enabled =
- file_util::GetFileSystemType(foreground_file, &foreground_type) &&
- file_util::GetFileSystemType(background_file, &background_type) &&
- foreground_type == file_util::FILE_SYSTEM_CGROUP &&
- background_type == file_util::FILE_SYSTEM_CGROUP;
+ base::GetFileSystemType(foreground_file, &foreground_type) &&
+ base::GetFileSystemType(background_file, &background_type) &&
+ foreground_type == FILE_SYSTEM_CGROUP &&
+ background_type == FILE_SYSTEM_CGROUP;
}
};
@@ -62,8 +64,6 @@ const int kBackgroundPriority = 5;
#endif
}
-namespace base {
-
bool Process::IsProcessBackgrounded() const {
DCHECK(process_);
@@ -95,7 +95,7 @@ bool Process::SetProcessBackgrounded(bool background) {
const base::FilePath file =
background ?
cgroups.Get().background_file : cgroups.Get().foreground_file;
- return file_util::WriteFile(file, pid.c_str(), pid.size()) > 0;
+ return base::WriteFile(file, pid.c_str(), pid.size()) > 0;
}
#endif // OS_CHROMEOS
diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc
index 83289b8c78b..95fb87ddf62 100644
--- a/chromium/base/process/process_metrics.cc
+++ b/chromium/base/process/process_metrics.cc
@@ -4,6 +4,7 @@
#include "base/process/process_metrics.h"
+#include "base/logging.h"
#include "base/values.h"
namespace base {
@@ -50,4 +51,11 @@ double ProcessMetrics::GetPlatformIndependentCPUUsage() {
#endif
}
+#if !defined(OS_MACOSX)
+int ProcessMetrics::GetIdleWakeupsPerSecond() {
+ NOTIMPLEMENTED(); // http://crbug.com/20488
+ return 0;
+}
+#endif // !defined(OS_MACOSX)
+
} // namespace base
diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h
index 560490a9b8c..d7a49ab5948 100644
--- a/chromium/base/process/process_metrics.h
+++ b/chromium/base/process/process_metrics.h
@@ -168,6 +168,10 @@ class BASE_EXPORT ProcessMetrics {
// CPU, this method returns 50.
double GetCPUUsage();
+ // Returns the number of average idle cpu wakeups per second since the last
+ // call.
+ int GetIdleWakeupsPerSecond();
+
// Same as GetCPUUsage(), but will return consistent values on all platforms
// (cancelling the Windows exception mentioned above) by returning a value in
// the range of 0 to (100 * numCPUCores) everywhere.
@@ -201,9 +205,13 @@ class BASE_EXPORT ProcessMetrics {
// Used to store the previous times and CPU usage counts so we can
// compute the CPU usage between calls.
- int64 last_time_;
+ TimeTicks last_cpu_time_;
int64 last_system_time_;
+ // Same thing for idle wakeups.
+ TimeTicks last_idle_wakeups_time_;
+ int64 last_absolute_idle_wakeups_;
+
#if !defined(OS_IOS)
#if defined(OS_MACOSX)
// Queries the port provider if it's set.
@@ -211,7 +219,7 @@ class BASE_EXPORT ProcessMetrics {
PortProvider* port_provider_;
#elif defined(OS_POSIX)
- // Jiffie count at the last_time_ we updated.
+ // Jiffie count at the last_cpu_time_ we updated.
int last_cpu_;
#endif // defined(OS_POSIX)
#endif // !defined(OS_IOS)
@@ -227,6 +235,10 @@ BASE_EXPORT size_t GetSystemCommitCharge();
// Returns the maximum number of file descriptors that can be open by a process
// at once. If the number is unavailable, a conservative best guess is returned.
size_t GetMaxFds();
+
+// Sets the file descriptor soft limit to |max_descriptors| or the OS hard
+// limit, whichever is lower.
+BASE_EXPORT void SetFdLimit(unsigned int max_descriptors);
#endif // defined(OS_POSIX)
#if defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/chromium/base/process/process_metrics_freebsd.cc b/chromium/base/process/process_metrics_freebsd.cc
index 019454cd81a..9d4149de2ad 100644
--- a/chromium/base/process/process_metrics_freebsd.cc
+++ b/chromium/base/process/process_metrics_freebsd.cc
@@ -4,11 +4,16 @@
#include "base/process/process_metrics.h"
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <unistd.h>
+
+#include "base/sys_info.h"
+
namespace base {
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
- last_time_(0),
last_system_time_(0),
last_cpu_(0) {
processor_count_ = base::SysInfo::NumberOfProcessors();
@@ -81,11 +86,6 @@ double ProcessMetrics::GetCPUUsage() {
int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process_ };
size_t length = sizeof(info);
- struct timeval now;
- int retval = gettimeofday(&now, NULL);
- if (retval)
- return 0;
-
if (sysctl(mib, arraysize(mib), &info, &length, NULL, 0) < 0)
return 0;
diff --git a/chromium/base/process/process_metrics_ios.cc b/chromium/base/process/process_metrics_ios.cc
index 94c671901b6..405c373c9fd 100644
--- a/chromium/base/process/process_metrics_ios.cc
+++ b/chromium/base/process/process_metrics_ios.cc
@@ -61,4 +61,8 @@ size_t GetMaxFds() {
return static_cast<size_t>(max_fds);
}
+void SetFdLimit(unsigned int max_descriptors) {
+ // Unimplemented.
+}
+
} // namespace base
diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc
index afa88486a0b..0e12595f92e 100644
--- a/chromium/base/process/process_metrics_linux.cc
+++ b/chromium/base/process/process_metrics_linux.cc
@@ -36,7 +36,7 @@ static uint64 ReadFileToUint64(const base::FilePath file) {
std::string file_as_string;
if (!ReadFileToString(file, &file_as_string))
return 0;
- TrimWhitespaceASCII(file_as_string, TRIM_ALL, &file_as_string);
+ base::TrimWhitespaceASCII(file_as_string, base::TRIM_ALL, &file_as_string);
uint64 file_as_uint64 = 0;
if (!base::StringToUint64(file_as_string, &file_as_uint64))
return 0;
@@ -71,7 +71,8 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
std::string value_str;
tokenizer.token_piece().CopyToString(&value_str);
std::string value_str_trimmed;
- TrimWhitespaceASCII(value_str, TRIM_ALL, &value_str_trimmed);
+ base::TrimWhitespaceASCII(value_str, base::TRIM_ALL,
+ &value_str_trimmed);
std::vector<std::string> split_value_str;
SplitString(value_str_trimmed, ' ', &split_value_str);
if (split_value_str.size() != 2 || split_value_str[1] != "kB") {
@@ -181,20 +182,16 @@ bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const {
}
double ProcessMetrics::GetCPUUsage() {
- struct timeval now;
- int retval = gettimeofday(&now, NULL);
- if (retval)
- return 0;
- int64 time = TimeValToMicroseconds(now);
+ TimeTicks time = TimeTicks::Now();
- if (last_time_ == 0) {
+ if (last_cpu_ == 0) {
// First call, just set the last values.
- last_time_ = time;
+ last_cpu_time_ = time;
last_cpu_ = GetProcessCPU(process_);
return 0;
}
- int64 time_delta = time - last_time_;
+ int64 time_delta = (time - last_cpu_time_).InMicroseconds();
DCHECK_NE(time_delta, 0);
if (time_delta == 0)
return 0;
@@ -209,7 +206,7 @@ double ProcessMetrics::GetCPUUsage() {
int percentage = 100 * (cpu_time - last_cpu_time).InSecondsF() /
TimeDelta::FromMicroseconds(time_delta).InSecondsF();
- last_time_ = time;
+ last_cpu_time_ = time;
last_cpu_ = cpu;
return percentage;
@@ -262,7 +259,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
- last_time_(0),
last_system_time_(0),
last_cpu_(0) {
processor_count_ = base::SysInfo::NumberOfProcessors();
@@ -391,16 +387,16 @@ int ParseProcStatCPU(const std::string& input) {
if (proc_stats.size() <= internal::VM_STIME)
return -1;
- int utime = GetProcStatsFieldAsInt(proc_stats, internal::VM_UTIME);
- int stime = GetProcStatsFieldAsInt(proc_stats, internal::VM_STIME);
+ int utime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_UTIME);
+ int stime = GetProcStatsFieldAsInt64(proc_stats, internal::VM_STIME);
return utime + stime;
}
const char kProcSelfExe[] = "/proc/self/exe";
int GetNumberOfThreads(ProcessHandle process) {
- return internal::ReadProcStatsAndGetFieldAsInt(process,
- internal::VM_NUMTHREADS);
+ return internal::ReadProcStatsAndGetFieldAsInt64(process,
+ internal::VM_NUMTHREADS);
}
namespace {
diff --git a/chromium/base/process/process_metrics_mac.cc b/chromium/base/process/process_metrics_mac.cc
index 048735ed36b..ada04e1427f 100644
--- a/chromium/base/process/process_metrics_mac.cc
+++ b/chromium/base/process/process_metrics_mac.cc
@@ -11,9 +11,27 @@
#include "base/containers/hash_tables.h"
#include "base/logging.h"
+#include "base/mac/mach_logging.h"
#include "base/mac/scoped_mach_port.h"
#include "base/sys_info.h"
+#if !defined(TASK_POWER_INFO)
+// Doesn't exist in the 10.6 or 10.7 SDKs.
+#define TASK_POWER_INFO 21
+struct task_power_info {
+ uint64_t total_user;
+ uint64_t total_system;
+ uint64_t task_interrupt_wakeups;
+ uint64_t task_platform_idle_wakeups;
+ uint64_t task_timer_wakeups_bin_1;
+ uint64_t task_timer_wakeups_bin_2;
+};
+typedef struct task_power_info task_power_info_data_t;
+typedef struct task_power_info *task_power_info_t;
+#define TASK_POWER_INFO_COUNT ((mach_msg_type_number_t) \
+ (sizeof (task_power_info_data_t) / sizeof (natural_t)))
+#endif
+
namespace base {
namespace {
@@ -99,7 +117,6 @@ size_t ProcessMetrics::GetPeakWorkingSetSize() const {
// shared_bytes is the size of shared resident memory.
bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
size_t* shared_bytes) {
- kern_return_t kr;
size_t private_pages_count = 0;
size_t shared_pages_count = 0;
@@ -136,22 +153,26 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
vm_region_top_info_data_t info;
mach_msg_type_number_t info_count = VM_REGION_TOP_INFO_COUNT;
mach_port_t object_name;
- kr = mach_vm_region(task,
- &address,
- &size,
- VM_REGION_TOP_INFO,
- (vm_region_info_t)&info,
- &info_count,
- &object_name);
+ kern_return_t kr = mach_vm_region(task,
+ &address,
+ &size,
+ VM_REGION_TOP_INFO,
+ reinterpret_cast<vm_region_info_t>(&info),
+ &info_count,
+ &object_name);
if (kr == KERN_INVALID_ADDRESS) {
// We're at the end of the address space.
break;
} else if (kr != KERN_SUCCESS) {
- DLOG(ERROR) << "Calling mach_vm_region failed with error: "
- << mach_error_string(kr);
+ MACH_DLOG(ERROR, kr) << "mach_vm_region";
return false;
}
+ // The kernel always returns a null object for VM_REGION_TOP_INFO, but
+ // balance it with a deallocate in case this ever changes. See 10.9.2
+ // xnu-2422.90.20/osfmk/vm/vm_map.c vm_map_region.
+ mach_port_deallocate(mach_task_self(), object_name);
+
if (IsAddressInSharedRegion(address, cpu_type) &&
info.share_mode != SM_PRIVATE)
continue;
@@ -179,18 +200,10 @@ bool ProcessMetrics::GetMemoryBytes(size_t* private_bytes,
}
}
- vm_size_t page_size;
- kr = host_page_size(task, &page_size);
- if (kr != KERN_SUCCESS) {
- DLOG(ERROR) << "Failed to fetch host page size, error: "
- << mach_error_string(kr);
- return false;
- }
-
if (private_bytes)
- *private_bytes = private_pages_count * page_size;
+ *private_bytes = private_pages_count * PAGE_SIZE;
if (shared_bytes)
- *shared_bytes = shared_pages_count * page_size;
+ *shared_bytes = shared_pages_count * PAGE_SIZE;
return true;
}
@@ -218,16 +231,14 @@ double ProcessMetrics::GetCPUUsage() {
if (task == MACH_PORT_NULL)
return 0;
- kern_return_t kr;
-
// Libtop explicitly loops over the threads (libtop_pinfo_update_cpu_usage()
// in libtop.c), but this is more concise and gives the same results:
task_thread_times_info thread_info_data;
mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
- kr = task_info(task,
- TASK_THREAD_TIMES_INFO,
- reinterpret_cast<task_info_t>(&thread_info_data),
- &thread_info_count);
+ kern_return_t kr = task_info(task,
+ TASK_THREAD_TIMES_INFO,
+ reinterpret_cast<task_info_t>(&thread_info_data),
+ &thread_info_count);
if (kr != KERN_SUCCESS) {
// Most likely cause: |task| is a zombie.
return 0;
@@ -250,33 +261,69 @@ double ProcessMetrics::GetCPUUsage() {
timeradd(&user_timeval, &task_timeval, &task_timeval);
timeradd(&system_timeval, &task_timeval, &task_timeval);
- struct timeval now;
- int retval = gettimeofday(&now, NULL);
- if (retval)
- return 0;
-
- int64 time = TimeValToMicroseconds(now);
+ TimeTicks time = TimeTicks::Now();
int64 task_time = TimeValToMicroseconds(task_timeval);
- if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ if (last_system_time_ == 0) {
// First call, just set the last values.
+ last_cpu_time_ = time;
last_system_time_ = task_time;
- last_time_ = time;
return 0;
}
int64 system_time_delta = task_time - last_system_time_;
- int64 time_delta = time - last_time_;
+ int64 time_delta = (time - last_cpu_time_).InMicroseconds();
DCHECK_NE(0U, time_delta);
if (time_delta == 0)
return 0;
+ last_cpu_time_ = time;
last_system_time_ = task_time;
- last_time_ = time;
return static_cast<double>(system_time_delta * 100.0) / time_delta;
}
+int ProcessMetrics::GetIdleWakeupsPerSecond() {
+ mach_port_t task = TaskForPid(process_);
+ if (task == MACH_PORT_NULL)
+ return 0;
+
+ task_power_info power_info_data;
+ mach_msg_type_number_t power_info_count = TASK_POWER_INFO_COUNT;
+ kern_return_t kr = task_info(task,
+ TASK_POWER_INFO,
+ reinterpret_cast<task_info_t>(&power_info_data),
+ &power_info_count);
+ if (kr != KERN_SUCCESS) {
+ // Most likely cause: |task| is a zombie, or this is on a pre-10.8.4 system
+ // where TASK_POWER_INFO isn't supported yet.
+ return 0;
+ }
+ uint64_t absolute_idle_wakeups = power_info_data.task_platform_idle_wakeups;
+
+ TimeTicks time = TimeTicks::Now();
+
+ if (last_absolute_idle_wakeups_ == 0) {
+ // First call, just set the last values.
+ last_idle_wakeups_time_ = time;
+ last_absolute_idle_wakeups_ = absolute_idle_wakeups;
+ return 0;
+ }
+
+ int64 wakeups_delta = absolute_idle_wakeups - last_absolute_idle_wakeups_;
+ int64 time_delta = (time - last_idle_wakeups_time_).InMicroseconds();
+ DCHECK_NE(0U, time_delta);
+ if (time_delta == 0)
+ return 0;
+
+ last_idle_wakeups_time_ = time;
+ last_absolute_idle_wakeups_ = absolute_idle_wakeups;
+
+ // Round to average wakeups per second.
+ const int kMicrosecondsPerSecond = 1000 * 1000;
+ return (wakeups_delta * kMicrosecondsPerSecond + time_delta/2) / time_delta;
+}
+
bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
return false;
}
@@ -284,8 +331,8 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
ProcessMetrics::ProcessMetrics(ProcessHandle process,
ProcessMetrics::PortProvider* port_provider)
: process_(process),
- last_time_(0),
last_system_time_(0),
+ last_absolute_idle_wakeups_(0),
port_provider_(port_provider) {
processor_count_ = SysInfo::NumberOfProcessors();
}
@@ -301,25 +348,18 @@ mach_port_t ProcessMetrics::TaskForPid(ProcessHandle process) const {
// Bytes committed by the system.
size_t GetSystemCommitCharge() {
- base::mac::ScopedMachPort host(mach_host_self());
+ base::mac::ScopedMachSendRight host(mach_host_self());
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
vm_statistics_data_t data;
kern_return_t kr = host_statistics(host, HOST_VM_INFO,
reinterpret_cast<host_info_t>(&data),
&count);
- if (kr) {
- DLOG(WARNING) << "Failed to fetch host statistics.";
- return 0;
- }
-
- vm_size_t page_size;
- kr = host_page_size(host, &page_size);
- if (kr) {
- DLOG(ERROR) << "Failed to fetch host page size.";
+ if (kr != KERN_SUCCESS) {
+ MACH_DLOG(WARNING, kr) << "host_statistics";
return 0;
}
- return (data.active_count * page_size) / 1024;
+ return (data.active_count * PAGE_SIZE) / 1024;
}
} // namespace base
diff --git a/chromium/base/process/process_metrics_openbsd.cc b/chromium/base/process/process_metrics_openbsd.cc
index 36f607c0a9c..72927a1b578 100644
--- a/chromium/base/process/process_metrics_openbsd.cc
+++ b/chromium/base/process/process_metrics_openbsd.cc
@@ -106,22 +106,16 @@ static int GetProcessCPU(pid_t pid) {
}
double ProcessMetrics::GetCPUUsage() {
- struct timeval now;
+ TimeTicks time = TimeTicks::Now();
- int retval = gettimeofday(&now, NULL);
- if (retval)
- return 0;
-
- int64 time = TimeValToMicroseconds(now);
-
- if (last_time_ == 0) {
+ if (last_cpu_ == 0) {
// First call, just set the last values.
- last_time_ = time;
+ last_cpu_time_ = time;
last_cpu_ = GetProcessCPU(process_);
return 0;
}
- int64 time_delta = time - last_time_;
+ int64 time_delta = (time - last_cpu_time_).InMicroseconds();
DCHECK_NE(time_delta, 0);
if (time_delta == 0)
@@ -129,7 +123,7 @@ double ProcessMetrics::GetCPUUsage() {
int cpu = GetProcessCPU(process_);
- last_time_ = time;
+ last_cpu_time_ = time;
last_cpu_ = cpu;
double percentage = static_cast<double>((cpu * 100.0) / FSCALE);
@@ -139,7 +133,6 @@ double ProcessMetrics::GetCPUUsage() {
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
- last_time_(0),
last_system_time_(0),
last_cpu_(0) {
diff --git a/chromium/base/process/process_metrics_posix.cc b/chromium/base/process/process_metrics_posix.cc
index 531f6a40d70..ea79d7348ff 100644
--- a/chromium/base/process/process_metrics_posix.cc
+++ b/chromium/base/process/process_metrics_posix.cc
@@ -52,4 +52,21 @@ size_t GetMaxFds() {
return static_cast<size_t>(max_fds);
}
+
+void SetFdLimit(unsigned int max_descriptors) {
+ struct rlimit limits;
+ if (getrlimit(RLIMIT_NOFILE, &limits) == 0) {
+ unsigned int new_limit = max_descriptors;
+ if (limits.rlim_max > 0 && limits.rlim_max < max_descriptors) {
+ new_limit = limits.rlim_max;
+ }
+ limits.rlim_cur = new_limit;
+ if (setrlimit(RLIMIT_NOFILE, &limits) != 0) {
+ PLOG(INFO) << "Failed to set file descriptor limit";
+ }
+ } else {
+ PLOG(INFO) << "Failed to get file descriptor limit";
+ }
+}
+
} // namespace base
diff --git a/chromium/base/process/process_metrics_win.cc b/chromium/base/process/process_metrics_win.cc
index f42ea86feb7..b1810b4c921 100644
--- a/chromium/base/process/process_metrics_win.cc
+++ b/chromium/base/process/process_metrics_win.cc
@@ -195,14 +195,11 @@ static uint64 FileTimeToUTC(const FILETIME& ftime) {
}
double ProcessMetrics::GetCPUUsage() {
- FILETIME now;
FILETIME creation_time;
FILETIME exit_time;
FILETIME kernel_time;
FILETIME user_time;
- GetSystemTimeAsFileTime(&now);
-
if (!GetProcessTimes(process_, &creation_time, &exit_time,
&kernel_time, &user_time)) {
// We don't assert here because in some cases (such as in the Task Manager)
@@ -212,17 +209,18 @@ double ProcessMetrics::GetCPUUsage() {
}
int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
processor_count_;
- int64 time = FileTimeToUTC(now);
+ TimeTicks time = TimeTicks::Now();
- if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ if (last_system_time_ == 0) {
// First call, just set the last values.
last_system_time_ = system_time;
- last_time_ = time;
+ last_cpu_time_ = time;
return 0;
}
int64 system_time_delta = system_time - last_system_time_;
- int64 time_delta = time - last_time_;
+ // FILETIME is in 100-nanosecond units, so this needs microseconds times 10.
+ int64 time_delta = (time - last_cpu_time_).InMicroseconds() * 10;
DCHECK_NE(0U, time_delta);
if (time_delta == 0)
return 0;
@@ -232,7 +230,7 @@ double ProcessMetrics::GetCPUUsage() {
time_delta);
last_system_time_ = system_time;
- last_time_ = time;
+ last_cpu_time_ = time;
return cpu;
}
@@ -269,7 +267,6 @@ bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const {
ProcessMetrics::ProcessMetrics(ProcessHandle process)
: process_(process),
processor_count_(base::SysInfo::NumberOfProcessors()),
- last_time_(0),
last_system_time_(0) {
}
diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc
index 6bfc1d07388..20623a60e18 100644
--- a/chromium/base/process/process_util_unittest.cc
+++ b/chromium/base/process/process_util_unittest.cc
@@ -144,8 +144,9 @@ MULTIPROCESS_TEST_MAIN(SimpleChildProcess) {
return 0;
}
+// TODO(viettrungluu): This should be in a "MultiProcessTestTest".
TEST_F(ProcessUtilTest, SpawnChild) {
- base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ base::ProcessHandle handle = SpawnChild("SimpleChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
EXPECT_TRUE(base::WaitForSingleProcess(
handle, TestTimeouts::action_max_timeout()));
@@ -161,7 +162,7 @@ TEST_F(ProcessUtilTest, KillSlowChild) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
remove(signal_file.c_str());
- base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ base::ProcessHandle handle = SpawnChild("SlowChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
SignalChildren(signal_file.c_str());
EXPECT_TRUE(base::WaitForSingleProcess(
@@ -175,7 +176,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileSlow);
remove(signal_file.c_str());
- base::ProcessHandle handle = this->SpawnChild("SlowChildProcess", false);
+ base::ProcessHandle handle = SpawnChild("SlowChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
int exit_code = 42;
@@ -198,7 +199,7 @@ TEST_F(ProcessUtilTest, DISABLED_GetTerminationStatusExit) {
TEST_F(ProcessUtilTest, GetProcId) {
base::ProcessId id1 = base::GetProcId(GetCurrentProcess());
EXPECT_NE(0ul, id1);
- base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ base::ProcessHandle handle = SpawnChild("SimpleChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
base::ProcessId id2 = base::GetProcId(handle);
EXPECT_NE(0ul, id2);
@@ -233,8 +234,7 @@ MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
// This test intentionally crashes, so we don't need to run it under
// AddressSanitizer.
-// TODO(jschuh): crbug.com/175753 Fix this in Win64 bots.
-#if defined(ADDRESS_SANITIZER) || (defined(OS_WIN) && defined(ARCH_CPU_X86_64))
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
#define MAYBE_GetTerminationStatusCrash DISABLED_GetTerminationStatusCrash
#else
#define MAYBE_GetTerminationStatusCrash GetTerminationStatusCrash
@@ -243,8 +243,7 @@ TEST_F(ProcessUtilTest, MAYBE_GetTerminationStatusCrash) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileCrash);
remove(signal_file.c_str());
- base::ProcessHandle handle = this->SpawnChild("CrashingChildProcess",
- false);
+ base::ProcessHandle handle = SpawnChild("CrashingChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
int exit_code = 42;
@@ -291,8 +290,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
const std::string signal_file =
ProcessUtilTest::GetSignalFilePath(kSignalFileKill);
remove(signal_file.c_str());
- base::ProcessHandle handle = this->SpawnChild("KilledChildProcess",
- false);
+ base::ProcessHandle handle = SpawnChild("KilledChildProcess");
ASSERT_NE(base::kNullProcessHandle, handle);
int exit_code = 42;
@@ -322,7 +320,7 @@ TEST_F(ProcessUtilTest, GetTerminationStatusKill) {
// Note: a platform may not be willing or able to lower the priority of
// a process. The calls to SetProcessBackground should be noops then.
TEST_F(ProcessUtilTest, SetProcessBackgrounded) {
- base::ProcessHandle handle = this->SpawnChild("SimpleChildProcess", false);
+ base::ProcessHandle handle = SpawnChild("SimpleChildProcess");
base::Process process(handle);
int old_priority = process.GetPriority();
#if defined(OS_WIN)
@@ -393,8 +391,8 @@ TEST_F(ProcessUtilTest, LaunchAsUser) {
ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
base::LaunchOptions options;
options.as_user = token;
- EXPECT_TRUE(base::LaunchProcess(
- this->MakeCmdLine("SimpleChildProcess", false), options, NULL));
+ EXPECT_TRUE(base::LaunchProcess(MakeCmdLine("SimpleChildProcess"), options,
+ NULL));
}
static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle";
@@ -430,7 +428,7 @@ TEST_F(ProcessUtilTest, InheritSpecifiedHandles) {
base::LaunchOptions options;
options.handles_to_inherit = &handles_to_inherit;
- CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess", false);
+ CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess");
cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch,
base::Uint64ToString(reinterpret_cast<uint64>(event.handle())));
@@ -505,8 +503,10 @@ int ProcessUtilTest::CountOpenFDsInChild() {
base::FileHandleMappingVector fd_mapping_vec;
fd_mapping_vec.push_back(std::pair<int, int>(fds[1], kChildPipe));
- base::ProcessHandle handle = this->SpawnChild(
- "ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false);
+ base::LaunchOptions options;
+ options.fds_to_remap = &fd_mapping_vec;
+ base::ProcessHandle handle =
+ SpawnChildWithOptions("ProcessUtilsLeakFDChildProcess", options);
CHECK(handle);
int ret = IGNORE_EINTR(close(fds[1]));
DPCHECK(ret == 0);
@@ -517,7 +517,7 @@ int ProcessUtilTest::CountOpenFDsInChild() {
HANDLE_EINTR(read(fds[0], &num_open_files, sizeof(num_open_files)));
CHECK_EQ(bytes_read, static_cast<ssize_t>(sizeof(num_open_files)));
-#if defined(THREAD_SANITIZER) || defined(USE_HEAPCHECKER)
+#if defined(THREAD_SANITIZER)
// Compiler-based ThreadSanitizer makes this test slow.
CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(3)));
#else
@@ -562,15 +562,12 @@ TEST_F(ProcessUtilTest, MAYBE_FDRemapping) {
namespace {
-std::string TestLaunchProcess(const base::EnvironmentMap& env_changes,
+std::string TestLaunchProcess(const std::vector<std::string>& args,
+ const base::EnvironmentMap& env_changes,
+ const bool clear_environ,
const int clone_flags) {
- std::vector<std::string> args;
base::FileHandleMappingVector fds_to_remap;
- args.push_back(kPosixShell);
- args.push_back("-c");
- args.push_back("echo $BASE_TEST");
-
int fds[2];
PCHECK(pipe(fds) == 0);
@@ -578,6 +575,7 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes,
base::LaunchOptions options;
options.wait = true;
options.environ = env_changes;
+ options.clear_environ = clear_environ;
options.fds_to_remap = &fds_to_remap;
#if defined(OS_LINUX)
options.clone_flags = clone_flags;
@@ -589,7 +587,6 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes,
char buf[512];
const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
- PCHECK(n > 0);
PCHECK(IGNORE_EINTR(close(fds[0])) == 0);
@@ -609,37 +606,69 @@ const char kLargeString[] =
TEST_F(ProcessUtilTest, LaunchProcess) {
base::EnvironmentMap env_changes;
+ std::vector<std::string> echo_base_test;
+ echo_base_test.push_back(kPosixShell);
+ echo_base_test.push_back("-c");
+ echo_base_test.push_back("echo $BASE_TEST");
+
+ std::vector<std::string> print_env;
+ print_env.push_back("/usr/bin/env");
const int no_clone_flags = 0;
+ const bool no_clear_environ = false;
const char kBaseTest[] = "BASE_TEST";
env_changes[kBaseTest] = "bar";
- EXPECT_EQ("bar\n", TestLaunchProcess(env_changes, no_clone_flags));
+ EXPECT_EQ("bar\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
env_changes.clear();
EXPECT_EQ(0, setenv(kBaseTest, "testing", 1 /* override */));
- EXPECT_EQ("testing\n", TestLaunchProcess(env_changes, no_clone_flags));
+ EXPECT_EQ("testing\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
env_changes[kBaseTest] = std::string();
- EXPECT_EQ("\n", TestLaunchProcess(env_changes, no_clone_flags));
+ EXPECT_EQ("\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
env_changes[kBaseTest] = "foo";
- EXPECT_EQ("foo\n", TestLaunchProcess(env_changes, no_clone_flags));
+ EXPECT_EQ("foo\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
env_changes.clear();
EXPECT_EQ(0, setenv(kBaseTest, kLargeString, 1 /* override */));
EXPECT_EQ(std::string(kLargeString) + "\n",
- TestLaunchProcess(env_changes, no_clone_flags));
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
env_changes[kBaseTest] = "wibble";
- EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, no_clone_flags));
+ EXPECT_EQ("wibble\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, no_clone_flags));
#if defined(OS_LINUX)
// Test a non-trival value for clone_flags.
// Don't test on Valgrind as it has limited support for clone().
if (!RunningOnValgrind()) {
- EXPECT_EQ("wibble\n", TestLaunchProcess(env_changes, CLONE_FS | SIGCHLD));
+ EXPECT_EQ(
+ "wibble\n",
+ TestLaunchProcess(
+ echo_base_test, env_changes, no_clear_environ, CLONE_FS | SIGCHLD));
}
+
+ EXPECT_EQ(
+ "BASE_TEST=wibble\n",
+ TestLaunchProcess(
+ print_env, env_changes, true /* clear_environ */, no_clone_flags));
+ env_changes.clear();
+ EXPECT_EQ(
+ "",
+ TestLaunchProcess(
+ print_env, env_changes, true /* clear_environ */, no_clone_flags));
#endif
}
@@ -677,7 +706,13 @@ TEST_F(ProcessUtilTest, GetAppOutput) {
#endif // defined(OS_ANDROID)
}
-TEST_F(ProcessUtilTest, GetAppOutputRestricted) {
+// Flakes on Android, crbug.com/375840
+#if defined(OS_ANDROID)
+#define MAYBE_GetAppOutputRestricted DISABLED_GetAppOutputRestricted
+#else
+#define MAYBE_GetAppOutputRestricted GetAppOutputRestricted
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestricted) {
// Unfortunately, since we can't rely on the path, we need to know where
// everything is. So let's use /bin/sh, which is on every POSIX system, and
// its built-ins.
@@ -812,8 +847,7 @@ bool IsProcessDead(base::ProcessHandle child) {
}
TEST_F(ProcessUtilTest, DelayedTermination) {
- base::ProcessHandle child_process =
- SpawnChild("process_util_test_never_die", false);
+ base::ProcessHandle child_process = SpawnChild("process_util_test_never_die");
ASSERT_TRUE(child_process);
base::EnsureProcessTerminated(child_process);
base::WaitForSingleProcess(child_process, base::TimeDelta::FromSeconds(5));
@@ -832,7 +866,7 @@ MULTIPROCESS_TEST_MAIN(process_util_test_never_die) {
TEST_F(ProcessUtilTest, ImmediateTermination) {
base::ProcessHandle child_process =
- SpawnChild("process_util_test_die_immediately", false);
+ SpawnChild("process_util_test_die_immediately");
ASSERT_TRUE(child_process);
// Give it time to die.
sleep(2);
diff --git a/chromium/base/rand_util.cc b/chromium/base/rand_util.cc
index 9641ff4f9c7..1525b91449d 100644
--- a/chromium/base/rand_util.cc
+++ b/chromium/base/rand_util.cc
@@ -61,16 +61,6 @@ uint64 RandGenerator(uint64 range) {
return value % range;
}
-void RandBytes(void* output, size_t output_length) {
- uint64 random_int;
- size_t random_int_size = sizeof(random_int);
- for (size_t i = 0; i < output_length; i += random_int_size) {
- random_int = base::RandUint64();
- size_t copy_count = std::min(output_length - i, random_int_size);
- memcpy(((uint8*)output) + i, &random_int, copy_count);
- }
-}
-
std::string RandBytesAsString(size_t length) {
DCHECK_GT(length, 0u);
std::string result;
diff --git a/chromium/base/rand_util.h b/chromium/base/rand_util.h
index bae8c311782..6130c129b7f 100644
--- a/chromium/base/rand_util.h
+++ b/chromium/base/rand_util.h
@@ -39,10 +39,11 @@ BASE_EXPORT double BitsToOpenEndedUnitInterval(uint64 bits);
// See crypto/ for cryptographically secure random number generation APIs.
BASE_EXPORT void RandBytes(void* output, size_t output_length);
-// Fills a string of length |length| with with random data and returns it.
+// Fills a string of length |length| with random data and returns it.
// |length| should be nonzero.
//
// Note that this is a variation of |RandBytes| with a different return type.
+// The returned string is likely not ASCII/UTF-8. Use with care.
//
// WARNING:
// Do not use for security-sensitive purposes.
diff --git a/chromium/base/rand_util_nacl.cc b/chromium/base/rand_util_nacl.cc
index 47450aaba68..b771dc44547 100644
--- a/chromium/base/rand_util_nacl.cc
+++ b/chromium/base/rand_util_nacl.cc
@@ -4,50 +4,38 @@
#include "base/rand_util.h"
+#include <nacl/nacl_random.h>
+
#include "base/basictypes.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "native_client/src/untrusted/irt/irt.h"
namespace {
-class NaclRandom {
- public:
- NaclRandom() {
- size_t result = nacl_interface_query(NACL_IRT_RANDOM_v0_1,
- &random_, sizeof(random_));
- CHECK_EQ(result, sizeof(random_));
- }
-
- ~NaclRandom() {
- }
-
- void GetRandomBytes(char* buffer, uint32_t num_bytes) {
- while (num_bytes > 0) {
- size_t nread;
- int error = random_.get_random_bytes(buffer, num_bytes, &nread);
- CHECK_EQ(error, 0);
- CHECK_LE(nread, num_bytes);
- buffer += nread;
- num_bytes -= nread;
- }
+void GetRandomBytes(void* output, size_t num_bytes) {
+ char* output_ptr = static_cast<char*>(output);
+ while (num_bytes > 0) {
+ size_t nread;
+ const int error = nacl_secure_random(output_ptr, num_bytes, &nread);
+ CHECK_EQ(error, 0);
+ CHECK_LE(nread, num_bytes);
+ output_ptr += nread;
+ num_bytes -= nread;
}
-
- private:
- nacl_irt_random random_;
-};
-
-base::LazyInstance<NaclRandom>::Leaky g_nacl_random = LAZY_INSTANCE_INITIALIZER;
+}
} // namespace
namespace base {
+// NOTE: This function must be cryptographically secure. http://crbug.com/140076
uint64 RandUint64() {
uint64 result;
- g_nacl_random.Pointer()->GetRandomBytes(
- reinterpret_cast<char*>(&result), sizeof(result));
+ GetRandomBytes(&result, sizeof(result));
return result;
}
+void RandBytes(void* output, size_t output_length) {
+ GetRandomBytes(output, output_length);
+}
+
} // namespace base
diff --git a/chromium/base/rand_util_posix.cc b/chromium/base/rand_util_posix.cc
index 082d64923d5..0a72a20d642 100644
--- a/chromium/base/rand_util_posix.cc
+++ b/chromium/base/rand_util_posix.cc
@@ -20,19 +20,16 @@ namespace {
// we can use LazyInstance to handle opening it on the first access.
class URandomFd {
public:
- URandomFd() {
- fd_ = open("/dev/urandom", O_RDONLY);
+ URandomFd() : fd_(open("/dev/urandom", O_RDONLY)) {
DCHECK_GE(fd_, 0) << "Cannot open /dev/urandom: " << errno;
}
- ~URandomFd() {
- close(fd_);
- }
+ ~URandomFd() { close(fd_); }
int fd() const { return fd_; }
private:
- int fd_;
+ const int fd_;
};
base::LazyInstance<URandomFd>::Leaky g_urandom_fd = LAZY_INSTANCE_INITIALIZER;
@@ -44,13 +41,15 @@ namespace base {
// NOTE: This function must be cryptographically secure. http://crbug.com/140076
uint64 RandUint64() {
uint64 number;
+ RandBytes(&number, sizeof(number));
+ return number;
+}
- int urandom_fd = g_urandom_fd.Pointer()->fd();
- bool success = ReadFromFD(urandom_fd, reinterpret_cast<char*>(&number),
- sizeof(number));
+void RandBytes(void* output, size_t output_length) {
+ const int urandom_fd = g_urandom_fd.Pointer()->fd();
+ const bool success =
+ ReadFromFD(urandom_fd, static_cast<char*>(output), output_length);
CHECK(success);
-
- return number;
}
int GetUrandomFD(void) {
diff --git a/chromium/base/rand_util_unittest.cc b/chromium/base/rand_util_unittest.cc
index e0e85ecaa91..d26227505d0 100644
--- a/chromium/base/rand_util_unittest.cc
+++ b/chromium/base/rand_util_unittest.cc
@@ -7,6 +7,9 @@
#include <algorithm>
#include <limits>
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
@@ -120,3 +123,22 @@ TEST(RandUtilTest, RandUint64ProducesBothValuesOfAllBits) {
FAIL() << "Didn't achieve all bit values in maximum number of tries.";
}
+
+// Benchmark test for RandBytes(). Disabled since it's intentionally slow and
+// does not test anything that isn't already tested by the existing RandBytes()
+// tests.
+TEST(RandUtilTest, DISABLED_RandBytesPerf) {
+ // Benchmark the performance of |kTestIterations| of RandBytes() using a
+ // buffer size of |kTestBufferSize|.
+ const int kTestIterations = 10;
+ const size_t kTestBufferSize = 1 * 1024 * 1024;
+
+ scoped_ptr<uint8[]> buffer(new uint8[kTestBufferSize]);
+ const base::TimeTicks now = base::TimeTicks::HighResNow();
+ for (int i = 0; i < kTestIterations; ++i)
+ base::RandBytes(buffer.get(), kTestBufferSize);
+ const base::TimeTicks end = base::TimeTicks::HighResNow();
+
+ LOG(INFO) << "RandBytes(" << kTestBufferSize << ") took: "
+ << (end - now).InMicroseconds() << "µs";
+}
diff --git a/chromium/base/rand_util_win.cc b/chromium/base/rand_util_win.cc
index 391fe5b9e61..8573b6b601b 100644
--- a/chromium/base/rand_util_win.cc
+++ b/chromium/base/rand_util_win.cc
@@ -4,28 +4,40 @@
#include "base/rand_util.h"
-#include <stdlib.h>
+#include <windows.h>
-#include "base/basictypes.h"
-#include "base/logging.h"
-
-namespace {
+// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the
+// "Community Additions" comment on MSDN here:
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx
+#define SystemFunction036 NTAPI SystemFunction036
+#include <NTSecAPI.h>
+#undef SystemFunction036
-uint32 RandUint32() {
- uint32 number;
- CHECK_EQ(rand_s(&number), 0);
- return number;
-}
+#include <algorithm>
+#include <limits>
-} // namespace
+#include "base/logging.h"
namespace base {
// NOTE: This function must be cryptographically secure. http://crbug.com/140076
uint64 RandUint64() {
- uint32 first_half = RandUint32();
- uint32 second_half = RandUint32();
- return (static_cast<uint64>(first_half) << 32) + second_half;
+ uint64 number;
+ RandBytes(&number, sizeof(number));
+ return number;
+}
+
+void RandBytes(void* output, size_t output_length) {
+ char* output_ptr = static_cast<char*>(output);
+ while (output_length > 0) {
+ const ULONG output_bytes_this_pass = static_cast<ULONG>(std::min(
+ output_length, static_cast<size_t>(std::numeric_limits<ULONG>::max())));
+ const bool success =
+ RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE;
+ CHECK(success);
+ output_length -= output_bytes_this_pass;
+ output_ptr += output_bytes_this_pass;
+ }
}
} // namespace base
diff --git a/chromium/base/run_loop.cc b/chromium/base/run_loop.cc
index 8666ee448fe..8344aa41b16 100644
--- a/chromium/base/run_loop.cc
+++ b/chromium/base/run_loop.cc
@@ -6,6 +6,10 @@
#include "base/bind.h"
+#if defined(OS_WIN)
+#include "base/message_loop/message_pump_dispatcher.h"
+#endif
+
namespace base {
RunLoop::RunLoop()
@@ -17,15 +21,13 @@ RunLoop::RunLoop()
running_(false),
quit_when_idle_received_(false),
weak_factory_(this) {
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
+#if defined(OS_WIN)
dispatcher_ = NULL;
#endif
}
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
-RunLoop::RunLoop(MessageLoop::Dispatcher* dispatcher)
+#if defined(OS_WIN)
+RunLoop::RunLoop(MessagePumpDispatcher* dispatcher)
: loop_(MessageLoop::current()),
previous_run_loop_(NULL),
dispatcher_(dispatcher),
diff --git a/chromium/base/run_loop.h b/chromium/base/run_loop.h
index 0dce6346e76..002410892f6 100644
--- a/chromium/base/run_loop.h
+++ b/chromium/base/run_loop.h
@@ -15,6 +15,10 @@ namespace base {
class MessagePumpForUI;
#endif
+#if defined(OS_WIN)
+class MessagePumpDispatcher;
+#endif
+
#if defined(OS_IOS)
class MessagePumpUIApplication;
#endif
@@ -27,19 +31,11 @@ class MessagePumpUIApplication;
class BASE_EXPORT RunLoop {
public:
RunLoop();
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
- explicit RunLoop(MessageLoop::Dispatcher* dispatcher);
+#if defined(OS_WIN)
+ explicit RunLoop(MessagePumpDispatcher* dispatcher);
#endif
~RunLoop();
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
- void set_dispatcher(MessageLoop::Dispatcher* dispatcher) {
- dispatcher_ = dispatcher;
- }
-#endif
-
// Run the current MessageLoop. This blocks until Quit is called. Before
// calling Run, be sure to grab an AsWeakPtr or the QuitClosure in order to
// stop the MessageLoop asynchronously. MessageLoop::Quit and QuitNow will
@@ -97,9 +93,8 @@ class BASE_EXPORT RunLoop {
// Parent RunLoop or NULL if this is the top-most RunLoop.
RunLoop* previous_run_loop_;
-#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
- !defined(USE_GTK_MESSAGE_PUMP)
- MessageLoop::Dispatcher* dispatcher_;
+#if defined(OS_WIN)
+ MessagePumpDispatcher* dispatcher_;
#endif
// Used to count how many nested Run() invocations are on the stack.
diff --git a/chromium/base/safe_numerics.h b/chromium/base/safe_numerics.h
deleted file mode 100644
index ce5c72fcc77..00000000000
--- a/chromium/base/safe_numerics.h
+++ /dev/null
@@ -1,135 +0,0 @@
-// Copyright 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_SAFE_NUMERICS_H_
-#define BASE_SAFE_NUMERICS_H_
-
-#include <limits>
-
-#include "base/logging.h"
-
-namespace base {
-namespace internal {
-
-template <bool SameSize, bool DestLarger,
- bool DestIsSigned, bool SourceIsSigned>
-struct IsValidNumericCastImpl;
-
-#define BASE_NUMERIC_CAST_CASE_SPECIALIZATION(A, B, C, D, Code) \
-template <> struct IsValidNumericCastImpl<A, B, C, D> { \
- template <class Source, class DestBounds> static inline bool Test( \
- Source source, DestBounds min, DestBounds max) { \
- return Code; \
- } \
-}
-
-#define BASE_NUMERIC_CAST_CASE_SAME_SIZE(DestSigned, SourceSigned, Code) \
- BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
- true, true, DestSigned, SourceSigned, Code); \
- BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
- true, false, DestSigned, SourceSigned, Code)
-
-#define BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(DestSigned, SourceSigned, Code) \
- BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
- false, false, DestSigned, SourceSigned, Code); \
-
-#define BASE_NUMERIC_CAST_CASE_DEST_LARGER(DestSigned, SourceSigned, Code) \
- BASE_NUMERIC_CAST_CASE_SPECIALIZATION( \
- false, true, DestSigned, SourceSigned, Code); \
-
-// The three top level cases are:
-// - Same size
-// - Source larger
-// - Dest larger
-// And for each of those three cases, we handle the 4 different possibilities
-// of signed and unsigned. This gives 12 cases to handle, which we enumerate
-// below.
-//
-// The last argument in each of the macros is the actual comparison code. It
-// has three arguments available, source (the value), and min/max which are
-// the ranges of the destination.
-
-
-// These are the cases where both types have the same size.
-
-// Both signed.
-BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, true, true);
-// Both unsigned.
-BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, false, true);
-// Dest unsigned, Source signed.
-BASE_NUMERIC_CAST_CASE_SAME_SIZE(false, true, source >= 0);
-// Dest signed, Source unsigned.
-// This cast is OK because Dest's max must be less than Source's.
-BASE_NUMERIC_CAST_CASE_SAME_SIZE(true, false,
- source <= static_cast<Source>(max));
-
-
-// These are the cases where Source is larger.
-
-// Both unsigned.
-BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, false, source <= max);
-// Both signed.
-BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, true,
- source >= min && source <= max);
-// Dest is unsigned, Source is signed.
-BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(false, true,
- source >= 0 && source <= max);
-// Dest is signed, Source is unsigned.
-// This cast is OK because Dest's max must be less than Source's.
-BASE_NUMERIC_CAST_CASE_SOURCE_LARGER(true, false,
- source <= static_cast<Source>(max));
-
-
-// These are the cases where Dest is larger.
-
-// Both unsigned.
-BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, false, true);
-// Both signed.
-BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, true, true);
-// Dest is unsigned, Source is signed.
-BASE_NUMERIC_CAST_CASE_DEST_LARGER(false, true, source >= 0);
-// Dest is signed, Source is unsigned.
-BASE_NUMERIC_CAST_CASE_DEST_LARGER(true, false, true);
-
-#undef BASE_NUMERIC_CAST_CASE_SPECIALIZATION
-#undef BASE_NUMERIC_CAST_CASE_SAME_SIZE
-#undef BASE_NUMERIC_CAST_CASE_SOURCE_LARGER
-#undef BASE_NUMERIC_CAST_CASE_DEST_LARGER
-
-
-// The main test for whether the conversion will under or overflow.
-template <class Dest, class Source>
-inline bool IsValidNumericCast(Source source) {
- typedef std::numeric_limits<Source> SourceLimits;
- typedef std::numeric_limits<Dest> DestLimits;
- COMPILE_ASSERT(SourceLimits::is_specialized, argument_must_be_numeric);
- COMPILE_ASSERT(SourceLimits::is_integer, argument_must_be_integral);
- COMPILE_ASSERT(DestLimits::is_specialized, result_must_be_numeric);
- COMPILE_ASSERT(DestLimits::is_integer, result_must_be_integral);
-
- return IsValidNumericCastImpl<
- sizeof(Dest) == sizeof(Source),
- (sizeof(Dest) > sizeof(Source)),
- DestLimits::is_signed,
- SourceLimits::is_signed>::Test(
- source,
- DestLimits::min(),
- DestLimits::max());
-}
-
-} // namespace internal
-
-// checked_numeric_cast<> is analogous to static_cast<> for numeric types,
-// except that it CHECKs that the specified numeric conversion will not
-// overflow or underflow. Floating point arguments are not currently allowed
-// (this is COMPILE_ASSERTd), though this could be supported if necessary.
-template <class Dest, class Source>
-inline Dest checked_numeric_cast(Source source) {
- CHECK(internal::IsValidNumericCast<Dest>(source));
- return static_cast<Dest>(source);
-}
-
-} // namespace base
-
-#endif // BASE_SAFE_NUMERICS_H_
diff --git a/chromium/base/safe_numerics_unittest.cc b/chromium/base/safe_numerics_unittest.cc
deleted file mode 100644
index c66e56f3cf7..00000000000
--- a/chromium/base/safe_numerics_unittest.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-// Copyright 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.
-
-#include <gtest/gtest.h>
-
-#include <sstream>
-#include <vector>
-
-#include "base/safe_numerics.h"
-
-namespace base {
-namespace internal {
-
-// This is far (far, far) too slow to run normally, but if you're refactoring
-// it might be useful.
-// #define RUN_EXHAUSTIVE_TEST
-
-#ifdef RUN_EXHAUSTIVE_TEST
-
-template <class From, class To> void ExhaustiveCheckFromTo() {
- fprintf(stderr, ".");
- From i = std::numeric_limits<From>::min();
- for (;;) {
- std::ostringstream str_from, str_to;
- str_from << i;
- To to = static_cast<To>(i);
- str_to << to;
- bool strings_equal = str_from.str() == str_to.str();
- EXPECT_EQ(IsValidNumericCast<To>(i), strings_equal);
- fprintf(stderr, "\r%s vs %s\x1B[K",
- str_from.str().c_str(), str_to.str().c_str());
- ++i;
- // If we wrap, then we've tested everything.
- if (i == std::numeric_limits<From>::min())
- break;
- }
-}
-
-template <class From> void ExhaustiveCheckFrom() {
- ExhaustiveCheckFromTo<From, short>();
- ExhaustiveCheckFromTo<From, unsigned short>();
- ExhaustiveCheckFromTo<From, int>();
- ExhaustiveCheckFromTo<From, unsigned int>();
- ExhaustiveCheckFromTo<From, long long>();
- ExhaustiveCheckFromTo<From, unsigned long long>();
- ExhaustiveCheckFromTo<From, size_t>();
- fprintf(stderr, "\n");
-}
-
-#endif
-
-
-TEST(SafeNumerics, NumericCast) {
- int small_positive = 1;
- int small_negative = -1;
- int large_positive = INT_MAX;
- int large_negative = INT_MIN;
- size_t size_t_small = 1;
- size_t size_t_large = UINT_MAX;
-
- // Narrow signed destination.
- EXPECT_TRUE(IsValidNumericCast<signed char>(small_positive));
- EXPECT_TRUE(IsValidNumericCast<signed char>(small_negative));
- EXPECT_FALSE(IsValidNumericCast<signed char>(large_positive));
- EXPECT_FALSE(IsValidNumericCast<signed char>(large_negative));
- EXPECT_TRUE(IsValidNumericCast<signed short>(small_positive));
- EXPECT_TRUE(IsValidNumericCast<signed short>(small_negative));
-
- // Narrow unsigned destination.
- EXPECT_TRUE(IsValidNumericCast<unsigned char>(small_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned char>(small_negative));
- EXPECT_FALSE(IsValidNumericCast<unsigned char>(large_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned char>(large_negative));
- EXPECT_FALSE(IsValidNumericCast<unsigned short>(small_negative));
- EXPECT_FALSE(IsValidNumericCast<unsigned short>(large_negative));
-
- // Same width signed destination.
- EXPECT_TRUE(IsValidNumericCast<signed int>(small_positive));
- EXPECT_TRUE(IsValidNumericCast<signed int>(small_negative));
- EXPECT_TRUE(IsValidNumericCast<signed int>(large_positive));
- EXPECT_TRUE(IsValidNumericCast<signed int>(large_negative));
-
- // Same width unsigned destination.
- EXPECT_TRUE(IsValidNumericCast<unsigned int>(small_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned int>(small_negative));
- EXPECT_TRUE(IsValidNumericCast<unsigned int>(large_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned int>(large_negative));
-
- // Wider signed destination.
- EXPECT_TRUE(IsValidNumericCast<long long>(small_positive));
- EXPECT_TRUE(IsValidNumericCast<long long>(large_negative));
- EXPECT_TRUE(IsValidNumericCast<long long>(small_positive));
- EXPECT_TRUE(IsValidNumericCast<long long>(large_negative));
-
- // Wider unsigned destination.
- EXPECT_TRUE(IsValidNumericCast<unsigned long long>(small_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned long long>(small_negative));
- EXPECT_TRUE(IsValidNumericCast<unsigned long long>(large_positive));
- EXPECT_FALSE(IsValidNumericCast<unsigned long long>(large_negative));
-
- // Negative to size_t.
- EXPECT_FALSE(IsValidNumericCast<size_t>(small_negative));
- EXPECT_FALSE(IsValidNumericCast<size_t>(large_negative));
-
- // From unsigned.
- // Small.
- EXPECT_TRUE(IsValidNumericCast<signed char>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<unsigned char>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<short>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<unsigned short>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<int>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<unsigned int>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<long long>(size_t_small));
- EXPECT_TRUE(IsValidNumericCast<unsigned long long>(size_t_small));
-
- // Large.
- EXPECT_FALSE(IsValidNumericCast<signed char>(size_t_large));
- EXPECT_FALSE(IsValidNumericCast<unsigned char>(size_t_large));
- EXPECT_FALSE(IsValidNumericCast<short>(size_t_large));
- EXPECT_FALSE(IsValidNumericCast<unsigned short>(size_t_large));
- EXPECT_FALSE(IsValidNumericCast<int>(size_t_large));
- EXPECT_TRUE(IsValidNumericCast<unsigned int>(size_t_large));
- EXPECT_TRUE(IsValidNumericCast<long long>(size_t_large));
- EXPECT_TRUE(IsValidNumericCast<unsigned long long>(size_t_large));
-
- // Various edge cases.
- EXPECT_TRUE(IsValidNumericCast<int>(static_cast<short>(SHRT_MIN)));
- EXPECT_FALSE(
- IsValidNumericCast<unsigned short>(static_cast<short>(SHRT_MIN)));
- EXPECT_FALSE(IsValidNumericCast<unsigned short>(SHRT_MIN));
-
- // Confirm that checked_numeric_cast<> actually compiles.
- std::vector<int> v;
- unsigned int checked_size =
- base::checked_numeric_cast<unsigned int>(v.size());
- EXPECT_EQ(0u, checked_size);
-
-#ifdef RUN_EXHAUSTIVE_TEST
- ExhaustiveCheckFrom<short>();
- ExhaustiveCheckFrom<unsigned short>();
- ExhaustiveCheckFrom<int>();
- ExhaustiveCheckFrom<unsigned int>();
- ExhaustiveCheckFrom<long long>();
- ExhaustiveCheckFrom<unsigned long long>();
- ExhaustiveCheckFrom<size_t>();
-#endif
-}
-
-} // namespace internal
-} // namespace base
diff --git a/chromium/base/safe_numerics_unittest.nc b/chromium/base/safe_numerics_unittest.nc
deleted file mode 100644
index 4219cd56a46..00000000000
--- a/chromium/base/safe_numerics_unittest.nc
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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.
-
-#include <float.h>
-
-#include "base/safe_numerics.h"
-
-using base::internal::IsValidNumericCast;
-
-#if defined(NCTEST_NO_FLOATING_POINT_1) // [r"size of array is negative"]
-
-void WontCompile() {
- IsValidNumericCast<float>(0.0);
-}
-
-#elif defined(NCTEST_NO_FLOATING_POINT_2) // [r"size of array is negative"]
-
-void WontCompile() {
- IsValidNumericCast<double>(0.0f);
-}
-
-#elif defined(NCTEST_NO_FLOATING_POINT_3) // [r"size of array is negative"]
-
-void WontCompile() {
- IsValidNumericCast<int>(DBL_MAX);
-}
-
-#endif
diff --git a/chromium/base/scoped_generic.h b/chromium/base/scoped_generic.h
new file mode 100644
index 00000000000..da42609e5c4
--- /dev/null
+++ b/chromium/base/scoped_generic.h
@@ -0,0 +1,176 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_SCOPED_GENERIC_H_
+#define BASE_SCOPED_GENERIC_H_
+
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "base/compiler_specific.h"
+#include "base/move.h"
+
+namespace base {
+
+// This class acts like ScopedPtr with a custom deleter (although is slightly
+// less fancy in some of the more escoteric respects) except that it keeps a
+// copy of the object rather than a pointer, and we require that the contained
+// object has some kind of "invalid" value.
+//
+// Defining a scoper based on this class allows you to get a scoper for
+// non-pointer types without having to write custom code for set, reset, and
+// move, etc. and get almost identical semantics that people are used to from
+// scoped_ptr.
+//
+// It is intended that you will typedef this class with an appropriate deleter
+// to implement clean up tasks for objects that act like pointers from a
+// resource management standpoint but aren't, such as file descriptors and
+// various types of operating system handles. Using scoped_ptr for these
+// things requires that you keep a pointer to the handle valid for the lifetime
+// of the scoper (which is easy to mess up).
+//
+// For an object to be able to be put into a ScopedGeneric, it must support
+// standard copyable semantics and have a specific "invalid" value. The traits
+// must define a free function and also the invalid value to assign for
+// default-constructed and released objects.
+//
+// struct FooScopedTraits {
+// // It's assumed that this is a fast inline function with little-to-no
+// // penalty for duplicate calls. This must be a static function even
+// // for stateful traits.
+// static int InvalidValue() {
+// return 0;
+// }
+//
+// // This free function will not be called if f == InvalidValue()!
+// static void Free(int f) {
+// ::FreeFoo(f);
+// }
+// };
+//
+// typedef ScopedGeneric<int, FooScopedTraits> ScopedFoo;
+template<typename T, typename Traits>
+class ScopedGeneric {
+ MOVE_ONLY_TYPE_FOR_CPP_03(ScopedGeneric, RValue)
+
+ private:
+ // This must be first since it's used inline below.
+ //
+ // Use the empty base class optimization to allow us to have a D
+ // member, while avoiding any space overhead for it when D is an
+ // empty class. See e.g. http://www.cantrip.org/emptyopt.html for a good
+ // discussion of this technique.
+ struct Data : public Traits {
+ explicit Data(const T& in) : generic(in) {}
+ Data(const T& in, const Traits& other) : Traits(other), generic(in) {}
+ T generic;
+ };
+
+ public:
+ typedef T element_type;
+ typedef Traits traits_type;
+
+ ScopedGeneric() : data_(traits_type::InvalidValue()) {}
+
+ // Constructor. Takes responsibility for freeing the resource associated with
+ // the object T.
+ explicit ScopedGeneric(const element_type& value) : data_(value) {}
+
+ // Constructor. Allows initialization of a stateful traits object.
+ ScopedGeneric(const element_type& value, const traits_type& traits)
+ : data_(value, traits) {
+ }
+
+ // Move constructor for C++03 move emulation.
+ ScopedGeneric(RValue rvalue)
+ : data_(rvalue.object->release(), rvalue.object->get_traits()) {
+ }
+
+ ~ScopedGeneric() {
+ FreeIfNecessary();
+ }
+
+ // Frees the currently owned object, if any. Then takes ownership of a new
+ // object, if given. Self-resets are not allowd as on scoped_ptr. See
+ // http://crbug.com/162971
+ void reset(const element_type& value = traits_type::InvalidValue()) {
+ if (data_.generic != traits_type::InvalidValue() && data_.generic == value)
+ abort();
+ FreeIfNecessary();
+ data_.generic = value;
+ }
+
+ void swap(ScopedGeneric& other) {
+ // Standard swap idiom: 'using std::swap' ensures that std::swap is
+ // present in the overload set, but we call swap unqualified so that
+ // any more-specific overloads can be used, if available.
+ using std::swap;
+ swap(static_cast<Traits&>(data_), static_cast<Traits&>(other.data_));
+ swap(data_.generic, other.data_.generic);
+ }
+
+ // Release the object. The return value is the current object held by this
+ // object. After this operation, this object will hold a null value, and
+ // will not own the object any more.
+ element_type release() WARN_UNUSED_RESULT {
+ element_type old_generic = data_.generic;
+ data_.generic = traits_type::InvalidValue();
+ return old_generic;
+ }
+
+ const element_type& get() const { return data_.generic; }
+
+ // Returns true if this object doesn't hold the special null value for the
+ // associated data type.
+ bool is_valid() const { return data_.generic != traits_type::InvalidValue(); }
+
+ bool operator==(const element_type& value) const {
+ return data_.generic == value;
+ }
+ bool operator!=(const element_type& value) const {
+ return data_.generic != value;
+ }
+
+ Traits& get_traits() { return data_; }
+ const Traits& get_traits() const { return data_; }
+
+ private:
+ void FreeIfNecessary() {
+ if (data_.generic != traits_type::InvalidValue()) {
+ data_.Free(data_.generic);
+ data_.generic = traits_type::InvalidValue();
+ }
+ }
+
+ // Forbid comparison. If U != T, it totally doesn't make sense, and if U ==
+ // T, it still doesn't make sense because you should never have the same
+ // object owned by two different ScopedGenerics.
+ template <typename T2, typename Traits2> bool operator==(
+ const ScopedGeneric<T2, Traits2>& p2) const;
+ template <typename T2, typename Traits2> bool operator!=(
+ const ScopedGeneric<T2, Traits2>& p2) const;
+
+ Data data_;
+};
+
+template<class T, class Traits>
+void swap(const ScopedGeneric<T, Traits>& a,
+ const ScopedGeneric<T, Traits>& b) {
+ a.swap(b);
+}
+
+template<class T, class Traits>
+bool operator==(const T& value, const ScopedGeneric<T, Traits>& scoped) {
+ return value == scoped.get();
+}
+
+template<class T, class Traits>
+bool operator!=(const T& value, const ScopedGeneric<T, Traits>& scoped) {
+ return value != scoped.get();
+}
+
+} // namespace base
+
+#endif // BASE_SCOPED_GENERIC_H_
diff --git a/chromium/base/scoped_generic_unittest.cc b/chromium/base/scoped_generic_unittest.cc
new file mode 100644
index 00000000000..f0dca22e693
--- /dev/null
+++ b/chromium/base/scoped_generic_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <vector>
+
+#include "base/scoped_generic.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+struct IntTraits {
+ IntTraits(std::vector<int>* freed) : freed_ints(freed) {}
+
+ static int InvalidValue() {
+ return -1;
+ }
+ void Free(int value) {
+ freed_ints->push_back(value);
+ }
+
+ std::vector<int>* freed_ints;
+};
+
+typedef ScopedGeneric<int, IntTraits> ScopedInt;
+
+} // namespace
+
+TEST(ScopedGenericTest, ScopedGeneric) {
+ std::vector<int> values_freed;
+ IntTraits traits(&values_freed);
+
+ // Invalid case, delete should not be called.
+ {
+ ScopedInt a(IntTraits::InvalidValue(), traits);
+ }
+ EXPECT_TRUE(values_freed.empty());
+
+ // Simple deleting case.
+ static const int kFirst = 0;
+ {
+ ScopedInt a(kFirst, traits);
+ }
+ ASSERT_EQ(1u, values_freed.size());
+ ASSERT_EQ(kFirst, values_freed[0]);
+ values_freed.clear();
+
+ // Release should return the right value and leave the object empty.
+ {
+ ScopedInt a(kFirst, traits);
+ EXPECT_EQ(kFirst, a.release());
+
+ ScopedInt b(IntTraits::InvalidValue(), traits);
+ EXPECT_EQ(IntTraits::InvalidValue(), b.release());
+ }
+ ASSERT_TRUE(values_freed.empty());
+
+ // Reset should free the old value, then the new one should go away when
+ // it goes out of scope.
+ static const int kSecond = 1;
+ {
+ ScopedInt b(kFirst, traits);
+ b.reset(kSecond);
+ ASSERT_EQ(1u, values_freed.size());
+ ASSERT_EQ(kFirst, values_freed[0]);
+ }
+ ASSERT_EQ(2u, values_freed.size());
+ ASSERT_EQ(kSecond, values_freed[1]);
+ values_freed.clear();
+
+ // Swap.
+ {
+ ScopedInt a(kFirst, traits);
+ ScopedInt b(kSecond, traits);
+ a.swap(b);
+ EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
+ EXPECT_EQ(kSecond, a.get());
+ EXPECT_EQ(kFirst, b.get());
+ }
+ // Values should be deleted in the opposite order.
+ ASSERT_EQ(2u, values_freed.size());
+ EXPECT_EQ(kFirst, values_freed[0]);
+ EXPECT_EQ(kSecond, values_freed[1]);
+ values_freed.clear();
+
+ // Pass.
+ {
+ ScopedInt a(kFirst, traits);
+ ScopedInt b(a.Pass());
+ EXPECT_TRUE(values_freed.empty()); // Nothing should be freed.
+ ASSERT_EQ(IntTraits::InvalidValue(), a.get());
+ ASSERT_EQ(kFirst, b.get());
+ }
+ ASSERT_EQ(1u, values_freed.size());
+ ASSERT_EQ(kFirst, values_freed[0]);
+}
+
+TEST(ScopedGenericTest, Operators) {
+ std::vector<int> values_freed;
+ IntTraits traits(&values_freed);
+
+ static const int kFirst = 0;
+ static const int kSecond = 1;
+ {
+ ScopedInt a(kFirst, traits);
+ EXPECT_TRUE(a == kFirst);
+ EXPECT_FALSE(a != kFirst);
+ EXPECT_FALSE(a == kSecond);
+ EXPECT_TRUE(a != kSecond);
+
+ EXPECT_TRUE(kFirst == a);
+ EXPECT_FALSE(kFirst != a);
+ EXPECT_FALSE(kSecond == a);
+ EXPECT_TRUE(kSecond != a);
+ }
+
+ // is_valid().
+ {
+ ScopedInt a(kFirst, traits);
+ EXPECT_TRUE(a.is_valid());
+ a.reset();
+ EXPECT_FALSE(a.is_valid());
+ }
+}
+
+// Cheesy manual "no compile" test for manually validating changes.
+#if 0
+TEST(ScopedGenericTest, NoCompile) {
+ // Assignment shouldn't work.
+ /*{
+ ScopedInt a(kFirst, traits);
+ ScopedInt b(a);
+ }*/
+
+ // Comparison shouldn't work.
+ /*{
+ ScopedInt a(kFirst, traits);
+ ScopedInt b(kFirst, traits);
+ if (a == b) {
+ }
+ }*/
+
+ // Implicit conversion to bool shouldn't work.
+ /*{
+ ScopedInt a(kFirst, traits);
+ bool result = a;
+ }*/
+}
+#endif
+
+} // namespace base
diff --git a/chromium/base/scoped_observer.h b/chromium/base/scoped_observer.h
index eae6367204a..3754ed57e1d 100644
--- a/chromium/base/scoped_observer.h
+++ b/chromium/base/scoped_observer.h
@@ -19,8 +19,7 @@ class ScopedObserver {
explicit ScopedObserver(Observer* observer) : observer_(observer) {}
~ScopedObserver() {
- for (size_t i = 0; i < sources_.size(); ++i)
- sources_[i]->RemoveObserver(observer_);
+ RemoveAll();
}
// Adds the object passed to the constructor as an observer on |source|.
@@ -35,12 +34,15 @@ class ScopedObserver {
source->RemoveObserver(observer_);
}
+ void RemoveAll() {
+ for (size_t i = 0; i < sources_.size(); ++i)
+ sources_[i]->RemoveObserver(observer_);
+ sources_.clear();
+ }
+
bool IsObserving(Source* source) const {
- for (size_t i = 0; i < sources_.size(); ++i) {
- if (sources_[i] == source)
- return true;
- }
- return false;
+ return std::find(sources_.begin(), sources_.end(), source) !=
+ sources_.end();
}
private:
diff --git a/chromium/base/security_unittest.cc b/chromium/base/security_unittest.cc
index cf3b1296667..960bc20276c 100644
--- a/chromium/base/security_unittest.cc
+++ b/chromium/base/security_unittest.cc
@@ -42,12 +42,12 @@ Type HideValueFromCompiler(volatile Type value) {
return value;
}
-// - NO_TCMALLOC (should be defined if we compile with linux_use_tcmalloc=0)
-// - ADDRESS_SANITIZER because it has its own memory allocator
+// - NO_TCMALLOC (should be defined if compiled with use_allocator!="tcmalloc")
+// - ADDRESS_SANITIZER and SYZYASAN because they have their own memory allocator
// - IOS does not use tcmalloc
// - OS_MACOSX does not use tcmalloc
#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \
- !defined(OS_IOS) && !defined(OS_MACOSX)
+ !defined(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN)
#define TCMALLOC_TEST(function) function
#else
#define TCMALLOC_TEST(function) DISABLED_##function
@@ -59,7 +59,7 @@ const size_t kTooBigAllocSize = INT_MAX;
// Detect runtime TCMalloc bypasses.
bool IsTcMallocBypassed() {
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#if defined(OS_LINUX)
// This should detect a TCMalloc bypass from Valgrind.
char* g_slice = getenv("G_SLICE");
if (g_slice && !strcmp(g_slice, "always-malloc"))
@@ -78,8 +78,10 @@ bool CallocDiesOnOOM() {
// The sanitizers' calloc dies on OOM instead of returning NULL.
// The wrapper function in base/process_util_linux.cc that is used when we
// compile without TCMalloc will just die on OOM instead of returning NULL.
-#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
- defined(THREAD_SANITIZER) || (defined(OS_LINUX) && defined(NO_TCMALLOC))
+#if defined(ADDRESS_SANITIZER) || \
+ defined(MEMORY_SANITIZER) || \
+ defined(THREAD_SANITIZER) || \
+ (defined(OS_LINUX) && defined(NO_TCMALLOC))
return true;
#else
return false;
@@ -229,7 +231,7 @@ TEST(SecurityTest, CallocOverflow) {
}
}
-#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__)
+#if defined(OS_LINUX) && defined(__x86_64__)
// Check if ptr1 and ptr2 are separated by less than size chars.
bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) {
ptrdiff_t ptr_diff = reinterpret_cast<char*>(std::max(ptr1, ptr2)) -
@@ -285,6 +287,6 @@ TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) {
EXPECT_FALSE(impossible_random_address);
}
-#endif // (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__)
+#endif // defined(OS_LINUX) && defined(__x86_64__)
} // namespace
diff --git a/chromium/base/sequence_checker.h b/chromium/base/sequence_checker.h
index 40d535e12d2..ad0182825c2 100644
--- a/chromium/base/sequence_checker.h
+++ b/chromium/base/sequence_checker.h
@@ -5,8 +5,6 @@
#ifndef BASE_SEQUENCE_CHECKER_H_
#define BASE_SEQUENCE_CHECKER_H_
-#include "base/memory/ref_counted.h"
-
// See comments for the similar block in thread_checker.h.
#if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON))
#define ENABLE_SEQUENCE_CHECKER 1
@@ -14,14 +12,10 @@
#define ENABLE_SEQUENCE_CHECKER 0
#endif
-#if ENABLE_SEQUENCE_CHECKER
#include "base/sequence_checker_impl.h"
-#endif
namespace base {
-class SequencedTaskRunner;
-
// Do nothing implementation, for use in release mode.
//
// Note: You should almost always use the SequenceChecker class to get
@@ -52,7 +46,7 @@ class SequenceCheckerDoNothing {
// SequenceChecker sequence_checker_;
// }
//
-// In Release mode, CalledOnValidSequence will always return true.
+// In Release mode, CalledOnValidSequencedThread() will always return true.
#if ENABLE_SEQUENCE_CHECKER
class SequenceChecker : public SequenceCheckerImpl {
};
diff --git a/chromium/base/sequence_checker_unittest.cc b/chromium/base/sequence_checker_unittest.cc
index 7df76149260..b8186408f60 100644
--- a/chromium/base/sequence_checker_unittest.cc
+++ b/chromium/base/sequence_checker_unittest.cc
@@ -261,8 +261,7 @@ TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInDebug) {
}, "");
}
#else
-TEST_F(SequenceCheckerTest,
- DifferentSequenceTokensDeathTestInRelease) {
+TEST_F(SequenceCheckerTest, DifferentSequenceTokensDeathTestInRelease) {
DifferentSequenceTokensDeathTest();
}
#endif // ENABLE_SEQUENCE_CHECKER
@@ -289,8 +288,7 @@ TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInDebug) {
}, "");
}
#else
-TEST_F(SequenceCheckerTest,
- WorkerPoolAndSimpleThreadDeathTestInRelease) {
+TEST_F(SequenceCheckerTest, WorkerPoolAndSimpleThreadDeathTestInRelease) {
WorkerPoolAndSimpleThreadDeathTest();
}
#endif // ENABLE_SEQUENCE_CHECKER
@@ -323,8 +321,7 @@ TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInDebug) {
}, "");
}
#else
-TEST_F(SequenceCheckerTest,
- TwoDifferentWorkerPoolsDeathTestInRelease) {
+TEST_F(SequenceCheckerTest, TwoDifferentWorkerPoolsDeathTestInRelease) {
TwoDifferentWorkerPoolsDeathTest();
}
#endif // ENABLE_SEQUENCE_CHECKER
diff --git a/chromium/base/sha1_win.cc b/chromium/base/sha1_win.cc
index 98c3840f0e4..b64c9eb8583 100644
--- a/chromium/base/sha1_win.cc
+++ b/chromium/base/sha1_win.cc
@@ -18,20 +18,20 @@ std::string SHA1HashString(const std::string& str) {
ScopedHCRYPTPROV provider;
if (!CryptAcquireContext(provider.receive(), NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)) {
- DLOG_GETLASTERROR(ERROR) << "CryptAcquireContext failed";
+ DPLOG(ERROR) << "CryptAcquireContext failed";
return std::string(kSHA1Length, '\0');
}
{
ScopedHCRYPTHASH hash;
if (!CryptCreateHash(provider, CALG_SHA1, 0, 0, hash.receive())) {
- DLOG_GETLASTERROR(ERROR) << "CryptCreateHash failed";
+ DPLOG(ERROR) << "CryptCreateHash failed";
return std::string(kSHA1Length, '\0');
}
if (!CryptHashData(hash, reinterpret_cast<CONST BYTE*>(str.data()),
static_cast<DWORD>(str.length()), 0)) {
- DLOG_GETLASTERROR(ERROR) << "CryptHashData failed";
+ DPLOG(ERROR) << "CryptHashData failed";
return std::string(kSHA1Length, '\0');
}
@@ -40,7 +40,7 @@ std::string SHA1HashString(const std::string& str) {
if (!CryptGetHashParam(hash, HP_HASHSIZE,
reinterpret_cast<unsigned char*>(&hash_len),
&buffer_size, 0)) {
- DLOG_GETLASTERROR(ERROR) << "CryptGetHashParam(HP_HASHSIZE) failed";
+ DPLOG(ERROR) << "CryptGetHashParam(HP_HASHSIZE) failed";
return std::string(kSHA1Length, '\0');
}
@@ -50,7 +50,7 @@ std::string SHA1HashString(const std::string& str) {
// but so that result.length() is correctly set to |hash_len|.
reinterpret_cast<BYTE*>(WriteInto(&result, hash_len + 1)), &hash_len,
0))) {
- DLOG_GETLASTERROR(ERROR) << "CryptGetHashParam(HP_HASHVAL) failed";
+ DPLOG(ERROR) << "CryptGetHashParam(HP_HASHVAL) failed";
return std::string(kSHA1Length, '\0');
}
diff --git a/chromium/base/stl_util.h b/chromium/base/stl_util.h
index ccad09d7317..3602df0377a 100644
--- a/chromium/base/stl_util.h
+++ b/chromium/base/stl_util.h
@@ -201,9 +201,11 @@ namespace base {
// Returns true if the container is sorted.
template <typename Container>
bool STLIsSorted(const Container& cont) {
- return std::adjacent_find(cont.begin(), cont.end(),
- std::greater<typename Container::value_type>())
- == cont.end();
+ // Note: Use reverse iterator on container to ensure we only require
+ // value_type to implement operator<.
+ return std::adjacent_find(cont.rbegin(), cont.rend(),
+ std::less<typename Container::value_type>())
+ == cont.rend();
}
// Returns a new ResultType containing the difference of two sorted containers.
@@ -218,6 +220,41 @@ ResultType STLSetDifference(const Arg1& a1, const Arg2& a2) {
return difference;
}
+// Returns a new ResultType containing the union of two sorted containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetUnion(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_union(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns a new ResultType containing the intersection of two sorted
+// containers.
+template <typename ResultType, typename Arg1, typename Arg2>
+ResultType STLSetIntersection(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ ResultType result;
+ std::set_intersection(a1.begin(), a1.end(),
+ a2.begin(), a2.end(),
+ std::inserter(result, result.end()));
+ return result;
+}
+
+// Returns true if the sorted container |a1| contains all elements of the sorted
+// container |a2|.
+template <typename Arg1, typename Arg2>
+bool STLIncludes(const Arg1& a1, const Arg2& a2) {
+ DCHECK(STLIsSorted(a1));
+ DCHECK(STLIsSorted(a2));
+ return std::includes(a1.begin(), a1.end(),
+ a2.begin(), a2.end());
+}
+
} // namespace base
#endif // BASE_STL_UTIL_H_
diff --git a/chromium/base/stl_util_unittest.cc b/chromium/base/stl_util_unittest.cc
index 63d5c5c1ee7..a3f8e16f2ce 100644
--- a/chromium/base/stl_util_unittest.cc
+++ b/chromium/base/stl_util_unittest.cc
@@ -8,6 +8,28 @@
#include "testing/gtest/include/gtest/gtest.h"
+namespace {
+
+// Used as test case to ensure the various base::STLXxx functions don't require
+// more than operators "<" and "==" on values stored in containers.
+class ComparableValue {
+ public:
+ explicit ComparableValue(int value) : value_(value) {}
+
+ bool operator==(const ComparableValue& rhs) const {
+ return value_ == rhs.value_;
+ }
+
+ bool operator<(const ComparableValue& rhs) const {
+ return value_ < rhs.value_;
+ }
+
+ private:
+ int value_;
+};
+
+}
+
namespace base {
namespace {
@@ -21,6 +43,14 @@ TEST(STLUtilTest, STLIsSorted) {
}
{
+ std::set<ComparableValue> set;
+ set.insert(ComparableValue(24));
+ set.insert(ComparableValue(1));
+ set.insert(ComparableValue(12));
+ EXPECT_TRUE(STLIsSorted(set));
+ }
+
+ {
std::vector<int> vector;
vector.push_back(1);
vector.push_back(1);
@@ -78,5 +108,135 @@ TEST(STLUtilTest, STLSetDifference) {
}
}
+TEST(STLUtilTest, STLSetUnion) {
+ std::set<int> a1;
+ a1.insert(1);
+ a1.insert(2);
+ a1.insert(3);
+ a1.insert(4);
+
+ std::set<int> a2;
+ a2.insert(3);
+ a2.insert(4);
+ a2.insert(5);
+ a2.insert(6);
+ a2.insert(7);
+
+ {
+ std::set<int> result;
+ result.insert(1);
+ result.insert(2);
+ result.insert(3);
+ result.insert(4);
+ result.insert(5);
+ result.insert(6);
+ result.insert(7);
+ EXPECT_EQ(result, STLSetUnion<std::set<int> >(a1, a2));
+ }
+
+ {
+ std::set<int> result;
+ result.insert(1);
+ result.insert(2);
+ result.insert(3);
+ result.insert(4);
+ result.insert(5);
+ result.insert(6);
+ result.insert(7);
+ EXPECT_EQ(result, STLSetUnion<std::set<int> >(a2, a1));
+ }
+
+ {
+ std::vector<int> result;
+ result.push_back(1);
+ result.push_back(2);
+ result.push_back(3);
+ result.push_back(4);
+ result.push_back(5);
+ result.push_back(6);
+ result.push_back(7);
+ EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a1, a2));
+ }
+
+ {
+ std::vector<int> result;
+ result.push_back(1);
+ result.push_back(2);
+ result.push_back(3);
+ result.push_back(4);
+ result.push_back(5);
+ result.push_back(6);
+ result.push_back(7);
+ EXPECT_EQ(result, STLSetUnion<std::vector<int> >(a2, a1));
+ }
+}
+
+TEST(STLUtilTest, STLSetIntersection) {
+ std::set<int> a1;
+ a1.insert(1);
+ a1.insert(2);
+ a1.insert(3);
+ a1.insert(4);
+
+ std::set<int> a2;
+ a2.insert(3);
+ a2.insert(4);
+ a2.insert(5);
+ a2.insert(6);
+ a2.insert(7);
+
+ {
+ std::set<int> result;
+ result.insert(3);
+ result.insert(4);
+ EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a1, a2));
+ }
+
+ {
+ std::set<int> result;
+ result.insert(3);
+ result.insert(4);
+ EXPECT_EQ(result, STLSetIntersection<std::set<int> >(a2, a1));
+ }
+
+ {
+ std::vector<int> result;
+ result.push_back(3);
+ result.push_back(4);
+ EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a1, a2));
+ }
+
+ {
+ std::vector<int> result;
+ result.push_back(3);
+ result.push_back(4);
+ EXPECT_EQ(result, STLSetIntersection<std::vector<int> >(a2, a1));
+ }
+}
+
+TEST(STLUtilTest, STLIncludes) {
+ std::set<int> a1;
+ a1.insert(1);
+ a1.insert(2);
+ a1.insert(3);
+ a1.insert(4);
+
+ std::set<int> a2;
+ a2.insert(3);
+ a2.insert(4);
+
+ std::set<int> a3;
+ a3.insert(3);
+ a3.insert(4);
+ a3.insert(5);
+
+ EXPECT_TRUE(STLIncludes<std::set<int> >(a1, a2));
+ EXPECT_FALSE(STLIncludes<std::set<int> >(a1, a3));
+ EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a1));
+ EXPECT_FALSE(STLIncludes<std::set<int> >(a2, a3));
+ EXPECT_FALSE(STLIncludes<std::set<int> >(a3, a1));
+ EXPECT_TRUE(STLIncludes<std::set<int> >(a3, a2));
+}
+
} // namespace
} // namespace base
diff --git a/chromium/base/strings/safe_sprintf_unittest.cc b/chromium/base/strings/safe_sprintf_unittest.cc
index 937fa4eca23..d9fb64689cc 100644
--- a/chromium/base/strings/safe_sprintf_unittest.cc
+++ b/chromium/base/strings/safe_sprintf_unittest.cc
@@ -416,7 +416,7 @@ void PrintLongString(char* buf, size_t sz) {
// The text that was generated by SafeSPrintf() should always match the
// equivalent text generated by sprintf(). Please note that the format
- // string for sprintf() is nor complicated, as it does not have the
+ // string for sprintf() is not complicated, as it does not have the
// benefit of getting type information from the C++ compiler.
//
// N.B.: It would be so much cleaner to use snprintf(). But unfortunately,
@@ -426,7 +426,8 @@ void PrintLongString(char* buf, size_t sz) {
CHECK_LE(sz, sizeof(ref));
sprintf(ref, "A long string: %%d 00DEADBEEF %lld 0x%llX <NULL>",
static_cast<long long>(std::numeric_limits<intptr_t>::min()),
- (long long)PrintLongString);
+ static_cast<unsigned long long>(
+ reinterpret_cast<uintptr_t>(PrintLongString)));
ref[sz-1] = '\000';
#if defined(NDEBUG)
diff --git a/chromium/base/strings/string16.h b/chromium/base/strings/string16.h
index fd98f1b5be7..804dca42e45 100644
--- a/chromium/base/strings/string16.h
+++ b/chromium/base/strings/string16.h
@@ -181,9 +181,4 @@ class BASE_EXPORT std::basic_string<base::char16, base::string16_char_traits>;
#endif // WCHAR_T_IS_UTF32
-// TODO(brettw) update users of string16 to use the namespace and remove
-// this "using".
-using base::char16;
-using base::string16;
-
#endif // BASE_STRINGS_STRING16_H_
diff --git a/chromium/base/strings/string_number_conversions.cc b/chromium/base/strings/string_number_conversions.cc
index e3e71bef023..59dc93be8ad 100644
--- a/chromium/base/strings/string_number_conversions.cc
+++ b/chromium/base/strings/string_number_conversions.cc
@@ -78,24 +78,19 @@ struct IntToStringT {
// unsigned, even the presence of the unary operation causes a warning.
UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value);
- for (typename STR::iterator it = outbuf.end();;) {
+ typename STR::iterator it(outbuf.end());
+ do {
--it;
DCHECK(it != outbuf.begin());
*it = static_cast<typename STR::value_type>((res % 10) + '0');
res /= 10;
-
- // We're done..
- if (res == 0) {
- if (is_neg) {
- --it;
- DCHECK(it != outbuf.begin());
- *it = static_cast<typename STR::value_type>('-');
- }
- return STR(it, outbuf.end());
- }
+ } while (res != 0);
+ if (is_neg) {
+ --it;
+ DCHECK(it != outbuf.begin());
+ *it = static_cast<typename STR::value_type>('-');
}
- NOTREACHED();
- return STR();
+ return STR(it, outbuf.end());
}
};
diff --git a/chromium/base/strings/string_piece.cc b/chromium/base/strings/string_piece.cc
index 79a42d78426..4c7f1122f2d 100644
--- a/chromium/base/strings/string_piece.cc
+++ b/chromium/base/strings/string_piece.cc
@@ -9,14 +9,30 @@
#include <ostream>
namespace base {
+namespace {
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+inline void BuildLookupTable(const StringPiece& characters_wanted,
+ bool* table) {
+ const size_t length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (size_t i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+} // namespace
// MSVC doesn't like complex extern templates and DLLs.
#if !defined(COMPILER_MSVC)
-namespace internal {
-template class StringPieceDetail<std::string>;
-template class StringPieceDetail<string16>;
-} // namespace internal
-
+template class BasicStringPiece<std::string>;
template class BasicStringPiece<string16>;
#endif
@@ -33,101 +49,153 @@ std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
}
namespace internal {
+
+template<typename STR>
+void CopyToStringT(const BasicStringPiece<STR>& self, STR* target) {
+ if (self.empty())
+ target->clear();
+ else
+ target->assign(self.data(), self.size());
+}
+
void CopyToString(const StringPiece& self, std::string* target) {
- target->assign(!self.empty() ? self.data() : "", self.size());
+ CopyToStringT(self, target);
}
-void AppendToString(const StringPiece& self, std::string* target) {
+void CopyToString(const StringPiece16& self, string16* target) {
+ CopyToStringT(self, target);
+}
+
+template<typename STR>
+void AppendToStringT(const BasicStringPiece<STR>& self, STR* target) {
if (!self.empty())
target->append(self.data(), self.size());
}
-StringPiece::size_type copy(const StringPiece& self,
- char* buf,
- StringPiece::size_type n,
- StringPiece::size_type pos) {
- StringPiece::size_type ret = std::min(self.size() - pos, n);
- memcpy(buf, self.data() + pos, ret);
+void AppendToString(const StringPiece& self, std::string* target) {
+ AppendToStringT(self, target);
+}
+
+void AppendToString(const StringPiece16& self, string16* target) {
+ AppendToStringT(self, target);
+}
+
+template<typename STR>
+size_t copyT(const BasicStringPiece<STR>& self,
+ typename STR::value_type* buf,
+ size_t n,
+ size_t pos) {
+ size_t ret = std::min(self.size() - pos, n);
+ memcpy(buf, self.data() + pos, ret * sizeof(typename STR::value_type));
return ret;
}
-StringPiece::size_type find(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+size_t copy(const StringPiece& self, char* buf, size_t n, size_t pos) {
+ return copyT(self, buf, n, pos);
+}
+
+size_t copy(const StringPiece16& self, char16* buf, size_t n, size_t pos) {
+ return copyT(self, buf, n, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+ const BasicStringPiece<STR>& s,
+ size_t pos) {
if (pos > self.size())
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
- StringPiece::const_iterator result =
+ typename BasicStringPiece<STR>::const_iterator result =
std::search(self.begin() + pos, self.end(), s.begin(), s.end());
- const StringPiece::size_type xpos =
+ const size_t xpos =
static_cast<size_t>(result - self.begin());
- return xpos + s.size() <= self.size() ? xpos : StringPiece::npos;
+ return xpos + s.size() <= self.size() ? xpos : BasicStringPiece<STR>::npos;
+}
+
+size_t find(const StringPiece& self, const StringPiece& s, size_t pos) {
+ return findT(self, s, pos);
}
-StringPiece::size_type find(const StringPiece& self,
- char c,
- StringPiece::size_type pos) {
+size_t find(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+ return findT(self, s, pos);
+}
+
+template<typename STR>
+size_t findT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
if (pos >= self.size())
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
- StringPiece::const_iterator result =
+ typename BasicStringPiece<STR>::const_iterator result =
std::find(self.begin() + pos, self.end(), c);
return result != self.end() ?
- static_cast<size_t>(result - self.begin()) : StringPiece::npos;
+ static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
}
-StringPiece::size_type rfind(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+size_t find(const StringPiece& self, char c, size_t pos) {
+ return findT(self, c, pos);
+}
+
+size_t find(const StringPiece16& self, char16 c, size_t pos) {
+ return findT(self, c, pos);
+}
+
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+ const BasicStringPiece<STR>& s,
+ size_t pos) {
if (self.size() < s.size())
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
if (s.empty())
return std::min(self.size(), pos);
- StringPiece::const_iterator last =
+ typename BasicStringPiece<STR>::const_iterator last =
self.begin() + std::min(self.size() - s.size(), pos) + s.size();
- StringPiece::const_iterator result =
+ typename BasicStringPiece<STR>::const_iterator result =
std::find_end(self.begin(), last, s.begin(), s.end());
return result != last ?
- static_cast<size_t>(result - self.begin()) : StringPiece::npos;
+ static_cast<size_t>(result - self.begin()) : BasicStringPiece<STR>::npos;
+}
+
+size_t rfind(const StringPiece& self, const StringPiece& s, size_t pos) {
+ return rfindT(self, s, pos);
+}
+
+size_t rfind(const StringPiece16& self, const StringPiece16& s, size_t pos) {
+ return rfindT(self, s, pos);
}
-StringPiece::size_type rfind(const StringPiece& self,
- char c,
- StringPiece::size_type pos) {
+template<typename STR>
+size_t rfindT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
if (self.size() == 0)
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
- for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ for (size_t i = std::min(pos, self.size() - 1); ;
+ --i) {
if (self.data()[i] == c)
return i;
if (i == 0)
break;
}
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
}
-// For each character in characters_wanted, sets the index corresponding
-// to the ASCII code of that character to 1 in table. This is used by
-// the find_.*_of methods below to tell whether or not a character is in
-// the lookup table in constant time.
-// The argument `table' must be an array that is large enough to hold all
-// the possible values of an unsigned char. Thus it should be be declared
-// as follows:
-// bool table[UCHAR_MAX + 1]
-static inline void BuildLookupTable(const StringPiece& characters_wanted,
- bool* table) {
- const StringPiece::size_type length = characters_wanted.length();
- const char* const data = characters_wanted.data();
- for (StringPiece::size_type i = 0; i < length; ++i) {
- table[static_cast<unsigned char>(data[i])] = true;
- }
+size_t rfind(const StringPiece& self, char c, size_t pos) {
+ return rfindT(self, c, pos);
}
-StringPiece::size_type find_first_of(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+size_t rfind(const StringPiece16& self, char16 c, size_t pos) {
+ return rfindT(self, c, pos);
+}
+
+// 8-bit version using lookup table.
+size_t find_first_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
if (self.size() == 0 || s.size() == 0)
return StringPiece::npos;
@@ -137,7 +205,7 @@ StringPiece::size_type find_first_of(const StringPiece& self,
bool lookup[UCHAR_MAX + 1] = { false };
BuildLookupTable(s, lookup);
- for (StringPiece::size_type i = pos; i < self.size(); ++i) {
+ for (size_t i = pos; i < self.size(); ++i) {
if (lookup[static_cast<unsigned char>(self.data()[i])]) {
return i;
}
@@ -145,9 +213,21 @@ StringPiece::size_type find_first_of(const StringPiece& self,
return StringPiece::npos;
}
-StringPiece::size_type find_first_not_of(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+// 16-bit brute force version.
+size_t find_first_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ StringPiece16::const_iterator found =
+ std::find_first_of(self.begin() + pos, self.end(), s.begin(), s.end());
+ if (found == self.end())
+ return StringPiece16::npos;
+ return found - self.begin();
+}
+
+// 8-bit version using lookup table.
+size_t find_first_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
if (self.size() == 0)
return StringPiece::npos;
@@ -160,7 +240,7 @@ StringPiece::size_type find_first_not_of(const StringPiece& self,
bool lookup[UCHAR_MAX + 1] = { false };
BuildLookupTable(s, lookup);
- for (StringPiece::size_type i = pos; i < self.size(); ++i) {
+ for (size_t i = pos; i < self.size(); ++i) {
if (!lookup[static_cast<unsigned char>(self.data()[i])]) {
return i;
}
@@ -168,23 +248,56 @@ StringPiece::size_type find_first_not_of(const StringPiece& self,
return StringPiece::npos;
}
-StringPiece::size_type find_first_not_of(const StringPiece& self,
- char c,
- StringPiece::size_type pos) {
+// 16-bit brute-force version.
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
if (self.size() == 0)
- return StringPiece::npos;
+ return StringPiece16::npos;
+
+ for (size_t self_i = pos; self_i < self.size(); ++self_i) {
+ bool found = false;
+ for (size_t s_i = 0; s_i < s.size(); ++s_i) {
+ if (self[self_i] == s[s_i]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return self_i;
+ }
+ return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_first_not_ofT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (self.size() == 0)
+ return BasicStringPiece<STR>::npos;
for (; pos < self.size(); ++pos) {
if (self.data()[pos] != c) {
return pos;
}
}
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
+}
+
+size_t find_first_not_of(const StringPiece& self,
+ char c,
+ size_t pos) {
+ return find_first_not_ofT(self, c, pos);
+}
+
+size_t find_first_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos) {
+ return find_first_not_ofT(self, c, pos);
}
-StringPiece::size_type find_last_of(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+// 8-bit version using lookup table.
+size_t find_last_of(const StringPiece& self, const StringPiece& s, size_t pos) {
if (self.size() == 0 || s.size() == 0)
return StringPiece::npos;
@@ -194,7 +307,7 @@ StringPiece::size_type find_last_of(const StringPiece& self,
bool lookup[UCHAR_MAX + 1] = { false };
BuildLookupTable(s, lookup);
- for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ for (size_t i = std::min(pos, self.size() - 1); ; --i) {
if (lookup[static_cast<unsigned char>(self.data()[i])])
return i;
if (i == 0)
@@ -203,13 +316,33 @@ StringPiece::size_type find_last_of(const StringPiece& self,
return StringPiece::npos;
}
-StringPiece::size_type find_last_not_of(const StringPiece& self,
- const StringPiece& s,
- StringPiece::size_type pos) {
+// 16-bit brute-force version.
+size_t find_last_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
+ if (self.size() == 0)
+ return StringPiece16::npos;
+
+ for (size_t self_i = std::min(pos, self.size() - 1); ;
+ --self_i) {
+ for (size_t s_i = 0; s_i < s.size(); s_i++) {
+ if (self.data()[self_i] == s[s_i])
+ return self_i;
+ }
+ if (self_i == 0)
+ break;
+ }
+ return StringPiece16::npos;
+}
+
+// 8-bit version using lookup table.
+size_t find_last_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos) {
if (self.size() == 0)
return StringPiece::npos;
- StringPiece::size_type i = std::min(pos, self.size() - 1);
+ size_t i = std::min(pos, self.size() - 1);
if (s.size() == 0)
return i;
@@ -228,27 +361,76 @@ StringPiece::size_type find_last_not_of(const StringPiece& self,
return StringPiece::npos;
}
-StringPiece::size_type find_last_not_of(const StringPiece& self,
- char c,
- StringPiece::size_type pos) {
+// 16-bit brute-force version.
+size_t find_last_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos) {
if (self.size() == 0)
return StringPiece::npos;
- for (StringPiece::size_type i = std::min(pos, self.size() - 1); ; --i) {
+ for (size_t self_i = std::min(pos, self.size() - 1); ; --self_i) {
+ bool found = false;
+ for (size_t s_i = 0; s_i < s.size(); s_i++) {
+ if (self.data()[self_i] == s[s_i]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return self_i;
+ if (self_i == 0)
+ break;
+ }
+ return StringPiece16::npos;
+}
+
+template<typename STR>
+size_t find_last_not_ofT(const BasicStringPiece<STR>& self,
+ typename STR::value_type c,
+ size_t pos) {
+ if (self.size() == 0)
+ return BasicStringPiece<STR>::npos;
+
+ for (size_t i = std::min(pos, self.size() - 1); ; --i) {
if (self.data()[i] != c)
return i;
if (i == 0)
break;
}
- return StringPiece::npos;
+ return BasicStringPiece<STR>::npos;
}
-StringPiece substr(const StringPiece& self,
- StringPiece::size_type pos,
- StringPiece::size_type n) {
+size_t find_last_not_of(const StringPiece& self,
+ char c,
+ size_t pos) {
+ return find_last_not_ofT(self, c, pos);
+}
+
+size_t find_last_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos) {
+ return find_last_not_ofT(self, c, pos);
+}
+
+template<typename STR>
+BasicStringPiece<STR> substrT(const BasicStringPiece<STR>& self,
+ size_t pos,
+ size_t n) {
if (pos > self.size()) pos = self.size();
if (n > self.size() - pos) n = self.size() - pos;
- return StringPiece(self.data() + pos, n);
+ return BasicStringPiece<STR>(self.data() + pos, n);
+}
+
+StringPiece substr(const StringPiece& self,
+ size_t pos,
+ size_t n) {
+ return substrT(self, pos, n);
+}
+
+StringPiece16 substr(const StringPiece16& self,
+ size_t pos,
+ size_t n) {
+ return substrT(self, pos, n);
}
} // namespace internal
diff --git a/chromium/base/strings/string_piece.h b/chromium/base/strings/string_piece.h
index 818d6ca598b..38e2277e98c 100644
--- a/chromium/base/strings/string_piece.h
+++ b/chromium/base/strings/string_piece.h
@@ -39,14 +39,124 @@ template <typename STRING_TYPE> class BasicStringPiece;
typedef BasicStringPiece<std::string> StringPiece;
typedef BasicStringPiece<string16> StringPiece16;
+// internal --------------------------------------------------------------------
+
+// Many of the StringPiece functions use different implementations for the
+// 8-bit and 16-bit versions, and we don't want lots of template expansions in
+// this (very common) header that will slow down compilation.
+//
+// So here we define overloaded functions called by the StringPiece template.
+// For those that share an implementation, the two versions will expand to a
+// template internal to the .cc file.
namespace internal {
+BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void CopyToString(const StringPiece16& self, string16* target);
+
+BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
+BASE_EXPORT void AppendToString(const StringPiece16& self, string16* target);
+
+BASE_EXPORT size_t copy(const StringPiece& self,
+ char* buf,
+ size_t n,
+ size_t pos);
+BASE_EXPORT size_t copy(const StringPiece16& self,
+ char16* buf,
+ size_t n,
+ size_t pos);
+
+BASE_EXPORT size_t find(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t rfind(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t rfind(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_first_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find_first_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece& self,
+ char c,
+ size_t pos);
+BASE_EXPORT size_t find_last_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+ const StringPiece& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+ const StringPiece16& s,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece16& self,
+ char16 c,
+ size_t pos);
+BASE_EXPORT size_t find_last_not_of(const StringPiece& self,
+ char c,
+ size_t pos);
+
+BASE_EXPORT StringPiece substr(const StringPiece& self,
+ size_t pos,
+ size_t n);
+BASE_EXPORT StringPiece16 substr(const StringPiece16& self,
+ size_t pos,
+ size_t n);
+
+} // namespace internal
+
+// BasicStringPiece ------------------------------------------------------------
+
// Defines the types, methods, operators, and data members common to both
// StringPiece and StringPiece16. Do not refer to this class directly, but
// rather to BasicStringPiece, StringPiece, or StringPiece16.
-template <typename STRING_TYPE> class StringPieceDetail {
+//
+// This is templatized by string class type rather than character type, so
+// BasicStringPiece<std::string> or BasicStringPiece<base::string16>.
+template <typename STRING_TYPE> class BasicStringPiece {
public:
- // standard STL container boilerplate
+ // Standard STL container boilerplate.
typedef size_t size_type;
typedef typename STRING_TYPE::value_type value_type;
typedef const value_type* pointer;
@@ -62,15 +172,15 @@ template <typename STRING_TYPE> class StringPieceDetail {
// We provide non-explicit singleton constructors so users can pass
// in a "const char*" or a "string" wherever a "StringPiece" is
// expected (likewise for char16, string16, StringPiece16).
- StringPieceDetail() : ptr_(NULL), length_(0) {}
- StringPieceDetail(const value_type* str)
+ BasicStringPiece() : ptr_(NULL), length_(0) {}
+ BasicStringPiece(const value_type* str)
: ptr_(str),
length_((str == NULL) ? 0 : STRING_TYPE::traits_type::length(str)) {}
- StringPieceDetail(const STRING_TYPE& str)
+ BasicStringPiece(const STRING_TYPE& str)
: ptr_(str.data()), length_(str.size()) {}
- StringPieceDetail(const value_type* offset, size_type len)
+ BasicStringPiece(const value_type* offset, size_type len)
: ptr_(offset), length_(len) {}
- StringPieceDetail(const typename STRING_TYPE::const_iterator& begin,
+ BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
const typename STRING_TYPE::const_iterator& end)
: ptr_((end > begin) ? &(*begin) : NULL),
length_((end > begin) ? (size_type)(end - begin) : 0) {}
@@ -141,213 +251,113 @@ template <typename STRING_TYPE> class StringPieceDetail {
return STRING_TYPE::traits_type::compare(p, p2, N);
}
- protected:
- const value_type* ptr_;
- size_type length_;
-};
-
-template <typename STRING_TYPE>
-const typename StringPieceDetail<STRING_TYPE>::size_type
-StringPieceDetail<STRING_TYPE>::npos =
- typename StringPieceDetail<STRING_TYPE>::size_type(-1);
-
-// MSVC doesn't like complex extern templates and DLLs.
-#if !defined(COMPILER_MSVC)
-extern template class BASE_EXPORT StringPieceDetail<std::string>;
-extern template class BASE_EXPORT StringPieceDetail<string16>;
-#endif
-
-BASE_EXPORT void CopyToString(const StringPiece& self, std::string* target);
-BASE_EXPORT void AppendToString(const StringPiece& self, std::string* target);
-BASE_EXPORT StringPieceDetail<std::string>::size_type copy(
- const StringPiece& self,
- char* buf,
- StringPieceDetail<std::string>::size_type n,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find(
- const StringPiece& self,
- char c,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type rfind(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type rfind(
- const StringPiece& self,
- char c,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_of(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_first_not_of(
- const StringPiece& self,
- char c,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_of(
- const StringPiece& self,
- char c,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of(
- const StringPiece& self,
- const StringPiece& s,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPieceDetail<std::string>::size_type find_last_not_of(
- const StringPiece& self,
- char c,
- StringPieceDetail<std::string>::size_type pos);
-BASE_EXPORT StringPiece substr(const StringPiece& self,
- StringPieceDetail<std::string>::size_type pos,
- StringPieceDetail<std::string>::size_type n);
-} // namespace internal
-
-// Defines the template type that is instantiated as either StringPiece or
-// StringPiece16.
-template <typename STRING_TYPE> class BasicStringPiece :
- public internal::StringPieceDetail<STRING_TYPE> {
- public:
- typedef typename internal::StringPieceDetail<STRING_TYPE>::value_type
- value_type;
- typedef typename internal::StringPieceDetail<STRING_TYPE>::size_type
- size_type;
-
- BasicStringPiece() {}
- BasicStringPiece(const value_type*str)
- : internal::StringPieceDetail<STRING_TYPE>(str) {}
- BasicStringPiece(const STRING_TYPE& str)
- : internal::StringPieceDetail<STRING_TYPE>(str) {}
- BasicStringPiece(const value_type* offset, size_type len)
- : internal::StringPieceDetail<STRING_TYPE>(offset, len) {}
- BasicStringPiece(const typename STRING_TYPE::const_iterator& begin,
- const typename STRING_TYPE::const_iterator& end)
- : internal::StringPieceDetail<STRING_TYPE>(begin, end) {}
-};
-
-// Specializes BasicStringPiece for std::string to add a few operations that
-// are not needed for string16.
-template <> class BasicStringPiece<std::string> :
- public internal::StringPieceDetail<std::string> {
- public:
- BasicStringPiece() {}
- BasicStringPiece(const char* str)
- : internal::StringPieceDetail<std::string>(str) {}
- BasicStringPiece(const std::string& str)
- : internal::StringPieceDetail<std::string>(str) {}
- BasicStringPiece(const char* offset, size_type len)
- : internal::StringPieceDetail<std::string>(offset, len) {}
- BasicStringPiece(const std::string::const_iterator& begin,
- const std::string::const_iterator& end)
- : internal::StringPieceDetail<std::string>(begin, end) {}
-
- // Prevent the following overload of set() from hiding the definitions in the
- // base class.
- using internal::StringPieceDetail<std::string>::set;
-
- void set(const void* data, size_type len) {
- ptr_ = reinterpret_cast<const value_type*>(data);
- length_ = len;
- }
-
- void CopyToString(std::string* target) const {
+ // Sets the value of the given string target type to be the current string.
+ // This saves a temporary over doing |a = b.as_string()|
+ void CopyToString(STRING_TYPE* target) const {
internal::CopyToString(*this, target);
}
- void AppendToString(std::string* target) const {
+ void AppendToString(STRING_TYPE* target) const {
internal::AppendToString(*this, target);
}
+ size_type copy(value_type* buf, size_type n, size_type pos = 0) const {
+ return internal::copy(*this, buf, n, pos);
+ }
+
// Does "this" start with "x"
bool starts_with(const BasicStringPiece& x) const {
- return ((length_ >= x.length_) &&
- (wordmemcmp(ptr_, x.ptr_, x.length_) == 0));
+ return ((this->length_ >= x.length_) &&
+ (wordmemcmp(this->ptr_, x.ptr_, x.length_) == 0));
}
// Does "this" end with "x"
bool ends_with(const BasicStringPiece& x) const {
- return ((length_ >= x.length_) &&
- (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
- }
-
- size_type copy(char* buf, size_type n, size_type pos = 0) const {
- return internal::copy(*this, buf, n, pos);
+ return ((this->length_ >= x.length_) &&
+ (wordmemcmp(this->ptr_ + (this->length_-x.length_),
+ x.ptr_, x.length_) == 0));
}
- size_type find(const BasicStringPiece& s, size_type pos = 0) const {
+ // find: Search for a character or substring at a given offset.
+ size_type find(const BasicStringPiece<STRING_TYPE>& s,
+ size_type pos = 0) const {
return internal::find(*this, s, pos);
}
-
- size_type find(char c, size_type pos = 0) const {
+ size_type find(value_type c, size_type pos = 0) const {
return internal::find(*this, c, pos);
}
- size_type rfind(const BasicStringPiece& s, size_type pos = npos) const {
+ // rfind: Reverse find.
+ size_type rfind(const BasicStringPiece& s,
+ size_type pos = BasicStringPiece::npos) const {
return internal::rfind(*this, s, pos);
}
-
- size_type rfind(char c, size_type pos = npos) const {
+ size_type rfind(value_type c, size_type pos = BasicStringPiece::npos) const {
return internal::rfind(*this, c, pos);
}
- size_type find_first_of(const BasicStringPiece& s, size_type pos = 0) const {
+ // find_first_of: Find the first occurence of one of a set of characters.
+ size_type find_first_of(const BasicStringPiece& s,
+ size_type pos = 0) const {
return internal::find_first_of(*this, s, pos);
}
-
- size_type find_first_of(char c, size_type pos = 0) const {
+ size_type find_first_of(value_type c, size_type pos = 0) const {
return find(c, pos);
}
+ // find_first_not_of: Find the first occurence not of a set of characters.
size_type find_first_not_of(const BasicStringPiece& s,
size_type pos = 0) const {
return internal::find_first_not_of(*this, s, pos);
}
-
- size_type find_first_not_of(char c, size_type pos = 0) const {
+ size_type find_first_not_of(value_type c, size_type pos = 0) const {
return internal::find_first_not_of(*this, c, pos);
}
+ // find_last_of: Find the last occurence of one of a set of characters.
size_type find_last_of(const BasicStringPiece& s,
- size_type pos = npos) const {
+ size_type pos = BasicStringPiece::npos) const {
return internal::find_last_of(*this, s, pos);
}
-
- size_type find_last_of(char c, size_type pos = npos) const {
+ size_type find_last_of(value_type c,
+ size_type pos = BasicStringPiece::npos) const {
return rfind(c, pos);
}
+ // find_last_not_of: Find the last occurence not of a set of characters.
size_type find_last_not_of(const BasicStringPiece& s,
- size_type pos = npos) const {
+ size_type pos = BasicStringPiece::npos) const {
return internal::find_last_not_of(*this, s, pos);
}
-
- size_type find_last_not_of(char c, size_type pos = npos) const {
+ size_type find_last_not_of(value_type c,
+ size_type pos = BasicStringPiece::npos) const {
return internal::find_last_not_of(*this, c, pos);
}
- BasicStringPiece substr(size_type pos, size_type n = npos) const {
+ // substr.
+ BasicStringPiece substr(size_type pos,
+ size_type n = BasicStringPiece::npos) const {
return internal::substr(*this, pos, n);
}
+
+ protected:
+ const value_type* ptr_;
+ size_type length_;
};
+template <typename STRING_TYPE>
+const typename BasicStringPiece<STRING_TYPE>::size_type
+BasicStringPiece<STRING_TYPE>::npos =
+ typename BasicStringPiece<STRING_TYPE>::size_type(-1);
+
// MSVC doesn't like complex extern templates and DLLs.
#if !defined(COMPILER_MSVC)
-// We can't explicitly declare the std::string instantiation here because it was
-// already instantiated when specialized, above. Not only is it a no-op, but
-// currently it also crashes Clang (see http://crbug.com/107412).
+extern template class BASE_EXPORT BasicStringPiece<std::string>;
extern template class BASE_EXPORT BasicStringPiece<string16>;
#endif
+// StingPiece operators --------------------------------------------------------
+
BASE_EXPORT bool operator==(const StringPiece& x, const StringPiece& y);
inline bool operator!=(const StringPiece& x, const StringPiece& y) {
@@ -372,6 +382,8 @@ inline bool operator>=(const StringPiece& x, const StringPiece& y) {
return !(x < y);
}
+// StringPiece16 operators -----------------------------------------------------
+
inline bool operator==(const StringPiece16& x, const StringPiece16& y) {
if (x.size() != y.size())
return false;
@@ -406,6 +418,8 @@ BASE_EXPORT std::ostream& operator<<(std::ostream& o,
} // namespace base
+// Hashing ---------------------------------------------------------------------
+
// We provide appropriate hash functions so StringPiece and StringPiece16 can
// be used as keys in hash sets and maps.
diff --git a/chromium/base/strings/string_piece_unittest.cc b/chromium/base/strings/string_piece_unittest.cc
index 84ed9efca8b..7f50cfbf679 100644
--- a/chromium/base/strings/string_piece_unittest.cc
+++ b/chromium/base/strings/string_piece_unittest.cc
@@ -192,20 +192,27 @@ TYPED_TEST(CommonStringPieceTest, CheckSTL) {
ASSERT_GE(a.capacity(), a.size());
}
-// STL stuff only supported by the std::string version
-TEST(StringPieceTest, CheckSTL) {
- StringPiece a("abcdefghijklmnopqrstuvwxyz");
- StringPiece b("abc");
- StringPiece c("xyz");
- StringPiece d("foobar");
+TYPED_TEST(CommonStringPieceTest, CheckFind) {
+ typedef BasicStringPiece<TypeParam> Piece;
+
+ TypeParam alphabet(TestFixture::as_string("abcdefghijklmnopqrstuvwxyz"));
+ TypeParam abc(TestFixture::as_string("abc"));
+ TypeParam xyz(TestFixture::as_string("xyz"));
+ TypeParam foobar(TestFixture::as_string("foobar"));
+
+ BasicStringPiece<TypeParam> a(alphabet);
+ BasicStringPiece<TypeParam> b(abc);
+ BasicStringPiece<TypeParam> c(xyz);
+ BasicStringPiece<TypeParam> d(foobar);
+
d.clear();
- StringPiece e;
- std::string temp("123");
- temp += '\0';
- temp += "456";
- StringPiece f(temp);
+ Piece e;
+ TypeParam temp(TestFixture::as_string("123"));
+ temp.push_back('\0');
+ temp += TestFixture::as_string("456");
+ Piece f(temp);
- char buf[4] = { '%', '%', '%', '%' };
+ typename TypeParam::value_type buf[4] = { '%', '%', '%', '%' };
ASSERT_EQ(a.copy(buf, 4), 4U);
ASSERT_EQ(buf[0], a[0]);
ASSERT_EQ(buf[1], a[1]);
@@ -222,28 +229,29 @@ TEST(StringPieceTest, CheckSTL) {
ASSERT_EQ(buf[2], c[2]);
ASSERT_EQ(buf[3], a[3]);
- ASSERT_EQ(StringPiece::npos, std::string::npos);
+ ASSERT_EQ(Piece::npos, TypeParam::npos);
ASSERT_EQ(a.find(b), 0U);
- ASSERT_EQ(a.find(b, 1), StringPiece::npos);
+ ASSERT_EQ(a.find(b, 1), Piece::npos);
ASSERT_EQ(a.find(c), 23U);
ASSERT_EQ(a.find(c, 9), 23U);
- ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
- ASSERT_EQ(b.find(c), StringPiece::npos);
- ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+ ASSERT_EQ(a.find(c, Piece::npos), Piece::npos);
+ ASSERT_EQ(b.find(c), Piece::npos);
+ ASSERT_EQ(b.find(c, Piece::npos), Piece::npos);
ASSERT_EQ(a.find(d), 0U);
ASSERT_EQ(a.find(e), 0U);
ASSERT_EQ(a.find(d, 12), 12U);
ASSERT_EQ(a.find(e, 17), 17U);
- StringPiece g("xx not found bb");
- ASSERT_EQ(a.find(g), StringPiece::npos);
+ TypeParam not_found(TestFixture::as_string("xx not found bb"));
+ Piece g(not_found);
+ ASSERT_EQ(a.find(g), Piece::npos);
// empty string nonsense
- ASSERT_EQ(d.find(b), StringPiece::npos);
- ASSERT_EQ(e.find(b), StringPiece::npos);
- ASSERT_EQ(d.find(b, 4), StringPiece::npos);
- ASSERT_EQ(e.find(b, 7), StringPiece::npos);
+ ASSERT_EQ(d.find(b), Piece::npos);
+ ASSERT_EQ(e.find(b), Piece::npos);
+ ASSERT_EQ(d.find(b, 4), Piece::npos);
+ ASSERT_EQ(e.find(b, 7), Piece::npos);
- size_t empty_search_pos = std::string().find(std::string());
+ size_t empty_search_pos = TypeParam().find(TypeParam());
ASSERT_EQ(d.find(d), empty_search_pos);
ASSERT_EQ(d.find(e), empty_search_pos);
ASSERT_EQ(e.find(d), empty_search_pos);
@@ -256,42 +264,42 @@ TEST(StringPieceTest, CheckSTL) {
ASSERT_EQ(a.find('a'), 0U);
ASSERT_EQ(a.find('c'), 2U);
ASSERT_EQ(a.find('z'), 25U);
- ASSERT_EQ(a.find('$'), StringPiece::npos);
- ASSERT_EQ(a.find('\0'), StringPiece::npos);
+ ASSERT_EQ(a.find('$'), Piece::npos);
+ ASSERT_EQ(a.find('\0'), Piece::npos);
ASSERT_EQ(f.find('\0'), 3U);
ASSERT_EQ(f.find('3'), 2U);
ASSERT_EQ(f.find('5'), 5U);
ASSERT_EQ(g.find('o'), 4U);
ASSERT_EQ(g.find('o', 4), 4U);
ASSERT_EQ(g.find('o', 5), 8U);
- ASSERT_EQ(a.find('b', 5), StringPiece::npos);
+ ASSERT_EQ(a.find('b', 5), Piece::npos);
// empty string nonsense
- ASSERT_EQ(d.find('\0'), StringPiece::npos);
- ASSERT_EQ(e.find('\0'), StringPiece::npos);
- ASSERT_EQ(d.find('\0', 4), StringPiece::npos);
- ASSERT_EQ(e.find('\0', 7), StringPiece::npos);
- ASSERT_EQ(d.find('x'), StringPiece::npos);
- ASSERT_EQ(e.find('x'), StringPiece::npos);
- ASSERT_EQ(d.find('x', 4), StringPiece::npos);
- ASSERT_EQ(e.find('x', 7), StringPiece::npos);
+ ASSERT_EQ(d.find('\0'), Piece::npos);
+ ASSERT_EQ(e.find('\0'), Piece::npos);
+ ASSERT_EQ(d.find('\0', 4), Piece::npos);
+ ASSERT_EQ(e.find('\0', 7), Piece::npos);
+ ASSERT_EQ(d.find('x'), Piece::npos);
+ ASSERT_EQ(e.find('x'), Piece::npos);
+ ASSERT_EQ(d.find('x', 4), Piece::npos);
+ ASSERT_EQ(e.find('x', 7), Piece::npos);
ASSERT_EQ(a.rfind(b), 0U);
ASSERT_EQ(a.rfind(b, 1), 0U);
ASSERT_EQ(a.rfind(c), 23U);
- ASSERT_EQ(a.rfind(c, 22U), StringPiece::npos);
- ASSERT_EQ(a.rfind(c, 1U), StringPiece::npos);
- ASSERT_EQ(a.rfind(c, 0U), StringPiece::npos);
- ASSERT_EQ(b.rfind(c), StringPiece::npos);
- ASSERT_EQ(b.rfind(c, 0U), StringPiece::npos);
- ASSERT_EQ(a.rfind(d), (size_t) a.as_string().rfind(std::string()));
- ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string()));
+ ASSERT_EQ(a.rfind(c, 22U), Piece::npos);
+ ASSERT_EQ(a.rfind(c, 1U), Piece::npos);
+ ASSERT_EQ(a.rfind(c, 0U), Piece::npos);
+ ASSERT_EQ(b.rfind(c), Piece::npos);
+ ASSERT_EQ(b.rfind(c, 0U), Piece::npos);
+ ASSERT_EQ(a.rfind(d), static_cast<size_t>(a.as_string().rfind(TypeParam())));
+ ASSERT_EQ(a.rfind(e), a.as_string().rfind(TypeParam()));
ASSERT_EQ(a.rfind(d, 12), 12U);
ASSERT_EQ(a.rfind(e, 17), 17U);
- ASSERT_EQ(a.rfind(g), StringPiece::npos);
- ASSERT_EQ(d.rfind(b), StringPiece::npos);
- ASSERT_EQ(e.rfind(b), StringPiece::npos);
- ASSERT_EQ(d.rfind(b, 4), StringPiece::npos);
- ASSERT_EQ(e.rfind(b, 7), StringPiece::npos);
+ ASSERT_EQ(a.rfind(g), Piece::npos);
+ ASSERT_EQ(d.rfind(b), Piece::npos);
+ ASSERT_EQ(e.rfind(b), Piece::npos);
+ ASSERT_EQ(d.rfind(b, 4), Piece::npos);
+ ASSERT_EQ(e.rfind(b, 7), Piece::npos);
// empty string nonsense
ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
@@ -303,80 +311,82 @@ TEST(StringPieceTest, CheckSTL) {
ASSERT_EQ(e.rfind(e), std::string().rfind(std::string()));
ASSERT_EQ(g.rfind('o'), 8U);
- ASSERT_EQ(g.rfind('q'), StringPiece::npos);
+ ASSERT_EQ(g.rfind('q'), Piece::npos);
ASSERT_EQ(g.rfind('o', 8), 8U);
ASSERT_EQ(g.rfind('o', 7), 4U);
- ASSERT_EQ(g.rfind('o', 3), StringPiece::npos);
+ ASSERT_EQ(g.rfind('o', 3), Piece::npos);
ASSERT_EQ(f.rfind('\0'), 3U);
ASSERT_EQ(f.rfind('\0', 12), 3U);
ASSERT_EQ(f.rfind('3'), 2U);
ASSERT_EQ(f.rfind('5'), 5U);
// empty string nonsense
- ASSERT_EQ(d.rfind('o'), StringPiece::npos);
- ASSERT_EQ(e.rfind('o'), StringPiece::npos);
- ASSERT_EQ(d.rfind('o', 4), StringPiece::npos);
- ASSERT_EQ(e.rfind('o', 7), StringPiece::npos);
-
- ASSERT_EQ(
- StringPiece("one,two:three;four").find_first_of(StringPiece(",:"), 1),
- 3U);
+ ASSERT_EQ(d.rfind('o'), Piece::npos);
+ ASSERT_EQ(e.rfind('o'), Piece::npos);
+ ASSERT_EQ(d.rfind('o', 4), Piece::npos);
+ ASSERT_EQ(e.rfind('o', 7), Piece::npos);
+
+ TypeParam one_two_three_four(TestFixture::as_string("one,two:three;four"));
+ TypeParam comma_colon(TestFixture::as_string(",:"));
+ ASSERT_EQ(3U, Piece(one_two_three_four).find_first_of(comma_colon));
ASSERT_EQ(a.find_first_of(b), 0U);
ASSERT_EQ(a.find_first_of(b, 0), 0U);
ASSERT_EQ(a.find_first_of(b, 1), 1U);
ASSERT_EQ(a.find_first_of(b, 2), 2U);
- ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(b, 3), Piece::npos);
ASSERT_EQ(a.find_first_of(c), 23U);
ASSERT_EQ(a.find_first_of(c, 23), 23U);
ASSERT_EQ(a.find_first_of(c, 24), 24U);
ASSERT_EQ(a.find_first_of(c, 25), 25U);
- ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(c, 26), Piece::npos);
ASSERT_EQ(g.find_first_of(b), 13U);
ASSERT_EQ(g.find_first_of(c), 0U);
- ASSERT_EQ(a.find_first_of(f), StringPiece::npos);
- ASSERT_EQ(f.find_first_of(a), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(f), Piece::npos);
+ ASSERT_EQ(f.find_first_of(a), Piece::npos);
// empty string nonsense
- ASSERT_EQ(a.find_first_of(d), StringPiece::npos);
- ASSERT_EQ(a.find_first_of(e), StringPiece::npos);
- ASSERT_EQ(d.find_first_of(b), StringPiece::npos);
- ASSERT_EQ(e.find_first_of(b), StringPiece::npos);
- ASSERT_EQ(d.find_first_of(d), StringPiece::npos);
- ASSERT_EQ(e.find_first_of(d), StringPiece::npos);
- ASSERT_EQ(d.find_first_of(e), StringPiece::npos);
- ASSERT_EQ(e.find_first_of(e), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(d), Piece::npos);
+ ASSERT_EQ(a.find_first_of(e), Piece::npos);
+ ASSERT_EQ(d.find_first_of(b), Piece::npos);
+ ASSERT_EQ(e.find_first_of(b), Piece::npos);
+ ASSERT_EQ(d.find_first_of(d), Piece::npos);
+ ASSERT_EQ(e.find_first_of(d), Piece::npos);
+ ASSERT_EQ(d.find_first_of(e), Piece::npos);
+ ASSERT_EQ(e.find_first_of(e), Piece::npos);
ASSERT_EQ(a.find_first_not_of(b), 3U);
ASSERT_EQ(a.find_first_not_of(c), 0U);
- ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos);
- ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(b.find_first_not_of(a), Piece::npos);
+ ASSERT_EQ(c.find_first_not_of(a), Piece::npos);
ASSERT_EQ(f.find_first_not_of(a), 0U);
ASSERT_EQ(a.find_first_not_of(f), 0U);
ASSERT_EQ(a.find_first_not_of(d), 0U);
ASSERT_EQ(a.find_first_not_of(e), 0U);
// empty string nonsense
- ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos);
- ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos);
- ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos);
- ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos);
- ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos);
- ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos);
-
- StringPiece h("====");
- ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos);
- ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of(a), Piece::npos);
+ ASSERT_EQ(e.find_first_not_of(a), Piece::npos);
+ ASSERT_EQ(d.find_first_not_of(d), Piece::npos);
+ ASSERT_EQ(e.find_first_not_of(d), Piece::npos);
+ ASSERT_EQ(d.find_first_not_of(e), Piece::npos);
+ ASSERT_EQ(e.find_first_not_of(e), Piece::npos);
+
+ TypeParam equals(TestFixture::as_string("===="));
+ Piece h(equals);
+ ASSERT_EQ(h.find_first_not_of('='), Piece::npos);
+ ASSERT_EQ(h.find_first_not_of('=', 3), Piece::npos);
ASSERT_EQ(h.find_first_not_of('\0'), 0U);
ASSERT_EQ(g.find_first_not_of('x'), 2U);
ASSERT_EQ(f.find_first_not_of('\0'), 0U);
ASSERT_EQ(f.find_first_not_of('\0', 3), 4U);
ASSERT_EQ(f.find_first_not_of('\0', 2), 2U);
// empty string nonsense
- ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos);
- ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos);
- ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
- ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
-
- // StringPiece g("xx not found bb");
- StringPiece i("56");
- ASSERT_EQ(h.find_last_of(a), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of('x'), Piece::npos);
+ ASSERT_EQ(e.find_first_not_of('x'), Piece::npos);
+ ASSERT_EQ(d.find_first_not_of('\0'), Piece::npos);
+ ASSERT_EQ(e.find_first_not_of('\0'), Piece::npos);
+
+ // Piece g("xx not found bb");
+ TypeParam fifty_six(TestFixture::as_string("56"));
+ Piece i(fifty_six);
+ ASSERT_EQ(h.find_last_of(a), Piece::npos);
ASSERT_EQ(g.find_last_of(a), g.size()-1);
ASSERT_EQ(a.find_last_of(b), 2U);
ASSERT_EQ(a.find_last_of(c), a.size()-1);
@@ -386,74 +396,74 @@ TEST(StringPieceTest, CheckSTL) {
ASSERT_EQ(a.find_last_of('z'), 25U);
ASSERT_EQ(a.find_last_of('a', 5), 0U);
ASSERT_EQ(a.find_last_of('b', 5), 1U);
- ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+ ASSERT_EQ(a.find_last_of('b', 0), Piece::npos);
ASSERT_EQ(a.find_last_of('z', 25), 25U);
- ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+ ASSERT_EQ(a.find_last_of('z', 24), Piece::npos);
ASSERT_EQ(f.find_last_of(i, 5), 5U);
ASSERT_EQ(f.find_last_of(i, 6), 6U);
- ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(a, 4), Piece::npos);
// empty string nonsense
- ASSERT_EQ(f.find_last_of(d), StringPiece::npos);
- ASSERT_EQ(f.find_last_of(e), StringPiece::npos);
- ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos);
- ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(d), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(e), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(d), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(e), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(f), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(f), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos);
- ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(d), Piece::npos);
+ ASSERT_EQ(f.find_last_of(e), Piece::npos);
+ ASSERT_EQ(f.find_last_of(d, 4), Piece::npos);
+ ASSERT_EQ(f.find_last_of(e, 4), Piece::npos);
+ ASSERT_EQ(d.find_last_of(d), Piece::npos);
+ ASSERT_EQ(d.find_last_of(e), Piece::npos);
+ ASSERT_EQ(e.find_last_of(d), Piece::npos);
+ ASSERT_EQ(e.find_last_of(e), Piece::npos);
+ ASSERT_EQ(d.find_last_of(f), Piece::npos);
+ ASSERT_EQ(e.find_last_of(f), Piece::npos);
+ ASSERT_EQ(d.find_last_of(d, 4), Piece::npos);
+ ASSERT_EQ(d.find_last_of(e, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_of(d, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_of(e, 4), Piece::npos);
+ ASSERT_EQ(d.find_last_of(f, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_of(f, 4), Piece::npos);
ASSERT_EQ(a.find_last_not_of(b), a.size()-1);
ASSERT_EQ(a.find_last_not_of(c), 22U);
- ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos);
- ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of(a), Piece::npos);
+ ASSERT_EQ(b.find_last_not_of(b), Piece::npos);
ASSERT_EQ(f.find_last_not_of(i), 4U);
ASSERT_EQ(a.find_last_not_of(c, 24), 22U);
ASSERT_EQ(a.find_last_not_of(b, 3), 3U);
- ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+ ASSERT_EQ(a.find_last_not_of(b, 2), Piece::npos);
// empty string nonsense
ASSERT_EQ(f.find_last_not_of(d), f.size()-1);
ASSERT_EQ(f.find_last_not_of(e), f.size()-1);
ASSERT_EQ(f.find_last_not_of(d, 4), 4U);
ASSERT_EQ(f.find_last_not_of(e, 4), 4U);
- ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(d), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of(e), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(d), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(e), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of(f), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(f), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of(d, 4), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of(e, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(d, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(e, 4), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of(f, 4), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of(f, 4), Piece::npos);
ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1);
- ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos);
+ ASSERT_EQ(h.find_last_not_of('='), Piece::npos);
ASSERT_EQ(b.find_last_not_of('c'), 1U);
ASSERT_EQ(h.find_last_not_of('x', 2), 2U);
- ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+ ASSERT_EQ(h.find_last_not_of('=', 2), Piece::npos);
ASSERT_EQ(b.find_last_not_of('b', 1), 0U);
// empty string nonsense
- ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos);
- ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
- ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of('x'), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of('x'), Piece::npos);
+ ASSERT_EQ(d.find_last_not_of('\0'), Piece::npos);
+ ASSERT_EQ(e.find_last_not_of('\0'), Piece::npos);
ASSERT_EQ(a.substr(0, 3), b);
ASSERT_EQ(a.substr(23), c);
ASSERT_EQ(a.substr(23, 3), c);
ASSERT_EQ(a.substr(23, 99), c);
ASSERT_EQ(a.substr(0), a);
- ASSERT_EQ(a.substr(3, 2), "de");
+ ASSERT_EQ(a.substr(3, 2), TestFixture::as_string("de"));
// empty string nonsense
ASSERT_EQ(a.substr(99, 2), e);
ASSERT_EQ(d.substr(99), e);
@@ -561,11 +571,11 @@ TEST(StringPieceTest, CheckCustom) {
ASSERT_TRUE(!e.ends_with(a));
StringPiece c;
- c.set(static_cast<const void*>("foobar"), 6);
+ c.set("foobar", 6);
ASSERT_EQ(c, a);
- c.set(static_cast<const void*>("foobar"), 0);
+ c.set("foobar", 0);
ASSERT_EQ(c, e);
- c.set(static_cast<const void*>("foobar"), 7);
+ c.set("foobar", 7);
ASSERT_NE(c, a);
}
@@ -664,9 +674,11 @@ TYPED_TEST(CommonStringPieceTest, CheckConstructors) {
ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.c_str()));
ASSERT_TRUE(TestFixture::as_string("hello") ==
BasicStringPiece<TypeParam>(str.c_str(), 5));
- ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.c_str(), 0U));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.c_str(),
+ static_cast<typename BasicStringPiece<TypeParam>::size_type>(0)));
ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL));
- ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL, 0U));
+ ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(NULL,
+ static_cast<typename BasicStringPiece<TypeParam>::size_type>(0)));
ASSERT_TRUE(empty == BasicStringPiece<TypeParam>());
ASSERT_TRUE(str == BasicStringPiece<TypeParam>(str.begin(), str.end()));
ASSERT_TRUE(empty == BasicStringPiece<TypeParam>(str.begin(), str.begin()));
diff --git a/chromium/base/strings/string_split.cc b/chromium/base/strings/string_split.cc
index 210789cd22d..7ef4760c58f 100644
--- a/chromium/base/strings/string_split.cc
+++ b/chromium/base/strings/string_split.cc
@@ -11,11 +11,13 @@
namespace base {
-template<typename STR>
-static void SplitStringT(const STR& str,
- const typename STR::value_type s,
- bool trim_whitespace,
- std::vector<STR>* r) {
+namespace {
+
+template <typename STR>
+void SplitStringT(const STR& str,
+ const typename STR::value_type s,
+ bool trim_whitespace,
+ std::vector<STR>* r) {
r->clear();
size_t last = 0;
size_t c = str.size();
@@ -33,87 +35,34 @@ static void SplitStringT(const STR& str,
}
}
-void SplitString(const string16& str,
- char16 c,
- std::vector<string16>* r) {
- DCHECK(CBU16_IS_SINGLE(c));
- SplitStringT(str, c, true, r);
-}
-
-void SplitString(const std::string& str,
- char c,
- std::vector<std::string>* r) {
-#if CHAR_MIN < 0
- DCHECK(c >= 0);
-#endif
- DCHECK(c < 0x7F);
- SplitStringT(str, c, true, r);
-}
-
-bool SplitStringIntoKeyValues(
- const std::string& line,
- char key_value_delimiter,
- std::string* key, std::vector<std::string>* values) {
+bool SplitStringIntoKeyValue(const std::string& line,
+ char key_value_delimiter,
+ std::string* key,
+ std::string* value) {
key->clear();
- values->clear();
+ value->clear();
- // Find the key string.
+ // Find the delimiter.
size_t end_key_pos = line.find_first_of(key_value_delimiter);
if (end_key_pos == std::string::npos) {
- DVLOG(1) << "cannot parse key from line: " << line;
- return false; // no key
+ DVLOG(1) << "cannot find delimiter in: " << line;
+ return false; // no delimiter
}
key->assign(line, 0, end_key_pos);
- // Find the values string.
+ // Find the value string.
std::string remains(line, end_key_pos, line.size() - end_key_pos);
- size_t begin_values_pos = remains.find_first_not_of(key_value_delimiter);
- if (begin_values_pos == std::string::npos) {
+ size_t begin_value_pos = remains.find_first_not_of(key_value_delimiter);
+ if (begin_value_pos == std::string::npos) {
DVLOG(1) << "cannot parse value from line: " << line;
return false; // no value
}
- std::string values_string(remains, begin_values_pos,
- remains.size() - begin_values_pos);
-
- // Construct the values vector.
- values->push_back(values_string);
+ value->assign(remains, begin_value_pos, remains.size() - begin_value_pos);
return true;
}
-bool SplitStringIntoKeyValuePairs(const std::string& line,
- char key_value_delimiter,
- char key_value_pair_delimiter,
- StringPairs* key_value_pairs) {
- key_value_pairs->clear();
-
- std::vector<std::string> pairs;
- SplitString(line, key_value_pair_delimiter, &pairs);
-
- bool success = true;
- for (size_t i = 0; i < pairs.size(); ++i) {
- // Empty pair. SplitStringIntoKeyValues is more strict about an empty pair
- // line, so continue with the next pair.
- if (pairs[i].empty())
- continue;
-
- std::string key;
- std::vector<std::string> value;
- if (!SplitStringIntoKeyValues(pairs[i],
- key_value_delimiter,
- &key, &value)) {
- // Don't return here, to allow for keys without associated
- // values; just record that our split failed.
- success = false;
- }
- DCHECK_LE(value.size(), 1U);
- key_value_pairs->push_back(
- make_pair(key, value.empty() ? std::string() : value[0]));
- }
- return success;
-}
-
template <typename STR>
-static void SplitStringUsingSubstrT(const STR& str,
+void SplitStringUsingSubstrT(const STR& str,
const STR& s,
std::vector<STR>* r) {
r->clear();
@@ -135,36 +84,6 @@ static void SplitStringUsingSubstrT(const STR& str,
}
}
-void SplitStringUsingSubstr(const string16& str,
- const string16& s,
- std::vector<string16>* r) {
- SplitStringUsingSubstrT(str, s, r);
-}
-
-void SplitStringUsingSubstr(const std::string& str,
- const std::string& s,
- std::vector<std::string>* r) {
- SplitStringUsingSubstrT(str, s, r);
-}
-
-void SplitStringDontTrim(const string16& str,
- char16 c,
- std::vector<string16>* r) {
- DCHECK(CBU16_IS_SINGLE(c));
- SplitStringT(str, c, false, r);
-}
-
-void SplitStringDontTrim(const std::string& str,
- char c,
- std::vector<std::string>* r) {
- DCHECK(IsStringUTF8(str));
-#if CHAR_MIN < 0
- DCHECK(c >= 0);
-#endif
- DCHECK(c < 0x7F);
- SplitStringT(str, c, false, r);
-}
-
template<typename STR>
void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
result->clear();
@@ -206,6 +125,82 @@ void SplitStringAlongWhitespaceT(const STR& str, std::vector<STR>* result) {
}
}
+} // namespace
+
+void SplitString(const string16& str,
+ char16 c,
+ std::vector<string16>* r) {
+ DCHECK(CBU16_IS_SINGLE(c));
+ SplitStringT(str, c, true, r);
+}
+
+void SplitString(const std::string& str,
+ char c,
+ std::vector<std::string>* r) {
+#if CHAR_MIN < 0
+ DCHECK(c >= 0);
+#endif
+ DCHECK(c < 0x7F);
+ SplitStringT(str, c, true, r);
+}
+
+bool SplitStringIntoKeyValuePairs(const std::string& line,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs) {
+ key_value_pairs->clear();
+
+ std::vector<std::string> pairs;
+ SplitString(line, key_value_pair_delimiter, &pairs);
+
+ bool success = true;
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ // Don't add empty pairs into the result.
+ if (pairs[i].empty())
+ continue;
+
+ std::string key;
+ std::string value;
+ if (!SplitStringIntoKeyValue(pairs[i], key_value_delimiter, &key, &value)) {
+ // Don't return here, to allow for pairs without associated
+ // value or key; just record that the split failed.
+ success = false;
+ }
+ key_value_pairs->push_back(make_pair(key, value));
+ }
+ return success;
+}
+
+void SplitStringUsingSubstr(const string16& str,
+ const string16& s,
+ std::vector<string16>* r) {
+ SplitStringUsingSubstrT(str, s, r);
+}
+
+void SplitStringUsingSubstr(const std::string& str,
+ const std::string& s,
+ std::vector<std::string>* r) {
+ SplitStringUsingSubstrT(str, s, r);
+}
+
+void SplitStringDontTrim(const string16& str,
+ char16 c,
+ std::vector<string16>* r) {
+ DCHECK(CBU16_IS_SINGLE(c));
+ SplitStringT(str, c, false, r);
+}
+
+void SplitStringDontTrim(const std::string& str,
+ char c,
+ std::vector<std::string>* r) {
+ DCHECK(IsStringUTF8(str));
+#if CHAR_MIN < 0
+ DCHECK(c >= 0);
+#endif
+ DCHECK(c < 0x7F);
+ SplitStringT(str, c, false, r);
+}
+
void SplitStringAlongWhitespace(const string16& str,
std::vector<string16>* result) {
SplitStringAlongWhitespaceT(str, result);
diff --git a/chromium/base/strings/string_split.h b/chromium/base/strings/string_split.h
index 7c27e4899da..55d8cb377ed 100644
--- a/chromium/base/strings/string_split.h
+++ b/chromium/base/strings/string_split.h
@@ -23,6 +23,7 @@ namespace base {
BASE_EXPORT void SplitString(const string16& str,
char16 c,
std::vector<string16>* r);
+
// |str| should not be in a multi-byte encoding like Shift-JIS or GBK in which
// the trailing byte of a multi-byte character can be in the ASCII range.
// UTF-8, and other single/multi-byte ASCII-compatible encodings are OK.
@@ -31,18 +32,16 @@ BASE_EXPORT void SplitString(const std::string& str,
char c,
std::vector<std::string>* r);
-BASE_EXPORT bool SplitStringIntoKeyValues(const std::string& line,
- char key_value_delimiter,
- std::string* key,
- std::vector<std::string>* values);
-
-typedef std::vector<std::pair<std::string, std::string> > StringPairs;;
+typedef std::vector<std::pair<std::string, std::string> > StringPairs;
-BASE_EXPORT bool SplitStringIntoKeyValuePairs(
- const std::string& line,
- char key_value_delimiter,
- char key_value_pair_delimiter,
- StringPairs* key_value_pairs);
+// Splits |line| into key value pairs according to the given delimiters and
+// removes whitespace leading each key and trailing each value. Returns true
+// only if each pair has a non-empty key and value. |key_value_pairs| will
+// include ("","") pairs for entries without |key_value_delimiter|.
+BASE_EXPORT bool SplitStringIntoKeyValuePairs(const std::string& line,
+ char key_value_delimiter,
+ char key_value_pair_delimiter,
+ StringPairs* key_value_pairs);
// The same as SplitString, but use a substring delimiter instead of a char.
BASE_EXPORT void SplitStringUsingSubstr(const string16& str,
diff --git a/chromium/base/strings/string_split_unittest.cc b/chromium/base/strings/string_split_unittest.cc
index eb69b68fcbd..9c932655e53 100644
--- a/chromium/base/strings/string_split_unittest.cc
+++ b/chromium/base/strings/string_split_unittest.cc
@@ -29,122 +29,131 @@ void SplitString(const std::wstring& str,
} // anonymous namespace
-class SplitStringIntoKeyValuesTest : public testing::Test {
+class SplitStringIntoKeyValuePairsTest : public testing::Test {
protected:
- std::string key;
- std::vector<std::string> values;
+ std::vector<std::pair<std::string, std::string> > kv_pairs;
};
-TEST_F(SplitStringIntoKeyValuesTest, EmptyInputMultipleValues) {
- EXPECT_FALSE(SplitStringIntoKeyValues(std::string(), // Empty input
- '\t', // Key separators
- &key,
- &values));
- EXPECT_TRUE(key.empty());
- EXPECT_TRUE(values.empty());
-}
-
-TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputMultipleValues) {
- EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t",
- '\t', // Key separators
- &key, &values));
- EXPECT_EQ("key_with_no_value", key);
- EXPECT_TRUE(values.empty());
-}
-
-TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputMultipleValues) {
- EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key",
- '\t', // Key separators
- &key, &values));
- EXPECT_TRUE(key.empty());
- ASSERT_EQ(1U, values.size());
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs(std::string(),
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ EXPECT_TRUE(kv_pairs.empty());
}
-TEST_F(SplitStringIntoKeyValuesTest, KeyWithMultipleValues) {
- EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3",
- '\t', // Key separators
- &key, &values));
- EXPECT_EQ("key1", key);
- ASSERT_EQ(1U, values.size());
- EXPECT_EQ("value1, value2 value3", values[0]);
+TEST_F(SplitStringIntoKeyValuePairsTest, MissingKeyValueDelimiter) {
+ EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1,key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_TRUE(kv_pairs[0].first.empty());
+ EXPECT_TRUE(kv_pairs[0].second.empty());
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
-TEST_F(SplitStringIntoKeyValuesTest, EmptyInputSingleValue) {
- EXPECT_FALSE(SplitStringIntoKeyValues(std::string(), // Empty input
- '\t', // Key separators
- &key,
- &values));
- EXPECT_TRUE(key.empty());
- EXPECT_TRUE(values.empty());
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyKeyWithKeyValueDelimiter) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs(":value1,key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_TRUE(kv_pairs[0].first.empty());
+ EXPECT_EQ("value1", kv_pairs[0].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
-TEST_F(SplitStringIntoKeyValuesTest, EmptyValueInputSingleValue) {
- EXPECT_FALSE(SplitStringIntoKeyValues("key_with_no_value\t",
- '\t', // Key separators
- &key, &values));
- EXPECT_EQ("key_with_no_value", key);
- EXPECT_TRUE(values.empty());
+TEST_F(SplitStringIntoKeyValuePairsTest, TrailingAndLeadingPairDelimiter) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs(",key1:value1,key2:value2,",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("value1", kv_pairs[0].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
-TEST_F(SplitStringIntoKeyValuesTest, EmptyKeyInputSingleValue) {
- EXPECT_TRUE(SplitStringIntoKeyValues("\tvalue for empty key",
- '\t', // Key separators
- &key, &values));
- EXPECT_TRUE(key.empty());
- ASSERT_EQ(1U, values.size());
- EXPECT_EQ("value for empty key", values[0]);
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyPair) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("value1", kv_pairs[0].second);
+ EXPECT_EQ("key3", kv_pairs[1].first);
+ EXPECT_EQ("value3", kv_pairs[1].second);
}
-TEST_F(SplitStringIntoKeyValuesTest, KeyWithSingleValue) {
- EXPECT_TRUE(SplitStringIntoKeyValues("key1\tvalue1, value2 value3",
- '\t', // Key separators
- &key, &values));
- EXPECT_EQ("key1", key);
- ASSERT_EQ(1U, values.size());
- EXPECT_EQ("value1, value2 value3", values[0]);
+TEST_F(SplitStringIntoKeyValuePairsTest, EmptyValue) {
+ EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:,key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ("key1", kv_pairs[0].first);
+ EXPECT_EQ("", kv_pairs[0].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
-class SplitStringIntoKeyValuePairsTest : public testing::Test {
- protected:
- std::vector<std::pair<std::string, std::string> > kv_pairs;
-};
-
-TEST_F(SplitStringIntoKeyValuePairsTest, EmptyString) {
- EXPECT_TRUE(SplitStringIntoKeyValuePairs(std::string(),
- ':', // Key-value delimiters
- ',', // Key-value pair delims
+TEST_F(SplitStringIntoKeyValuePairsTest, UntrimmedWhitespace) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1 : value1",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
&kv_pairs));
- EXPECT_TRUE(kv_pairs.empty());
+ ASSERT_EQ(1U, kv_pairs.size());
+ EXPECT_EQ("key1 ", kv_pairs[0].first);
+ EXPECT_EQ(" value1", kv_pairs[0].second);
}
-TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondPair) {
- EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1,,key3:value3",
- ':', // Key-value delimiters
- ',', // Key-value pair delims
+TEST_F(SplitStringIntoKeyValuePairsTest, TrimmedWhitespace) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:value1 , key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
&kv_pairs));
ASSERT_EQ(2U, kv_pairs.size());
EXPECT_EQ("key1", kv_pairs[0].first);
EXPECT_EQ("value1", kv_pairs[0].second);
- EXPECT_EQ("key3", kv_pairs[1].first);
- EXPECT_EQ("value3", kv_pairs[1].second);
+ EXPECT_EQ("key2", kv_pairs[1].first);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
-TEST_F(SplitStringIntoKeyValuePairsTest, EmptySecondValue) {
- EXPECT_FALSE(SplitStringIntoKeyValuePairs("key1:value1 , key2:",
- ':', // Key-value delimiters
- ',', // Key-value pair delims
- &kv_pairs));
+TEST_F(SplitStringIntoKeyValuePairsTest, MultipleKeyValueDelimiters) {
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:::value1,key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
+ &kv_pairs));
ASSERT_EQ(2U, kv_pairs.size());
EXPECT_EQ("key1", kv_pairs[0].first);
EXPECT_EQ("value1", kv_pairs[0].second);
EXPECT_EQ("key2", kv_pairs[1].first);
- EXPECT_EQ("", kv_pairs[1].second);
+ EXPECT_EQ("value2", kv_pairs[1].second);
}
+TEST_F(SplitStringIntoKeyValuePairsTest, OnlySplitAtGivenSeparator) {
+ std::string a("a ?!@#$%^&*()_+:/{}\\\t\nb");
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs(a + "X" + a + "Y" + a + "X" + a,
+ 'X', // Key-value delimiter
+ 'Y', // Key-value pair delimiter
+ &kv_pairs));
+ ASSERT_EQ(2U, kv_pairs.size());
+ EXPECT_EQ(a, kv_pairs[0].first);
+ EXPECT_EQ(a, kv_pairs[0].second);
+ EXPECT_EQ(a, kv_pairs[1].first);
+ EXPECT_EQ(a, kv_pairs[1].second);
+}
+
+
TEST_F(SplitStringIntoKeyValuePairsTest, DelimiterInValue) {
- EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1 , key2:value2",
- ':', // Key-value delimiters
- ',', // Key-value pair delims
+ EXPECT_TRUE(SplitStringIntoKeyValuePairs("key1:va:ue1,key2:value2",
+ ':', // Key-value delimiter
+ ',', // Key-value pair delimiter
&kv_pairs));
ASSERT_EQ(2U, kv_pairs.size());
EXPECT_EQ("key1", kv_pairs[0].first);
@@ -160,7 +169,6 @@ TEST(SplitStringUsingSubstrTest, EmptyString) {
EXPECT_THAT(results, ElementsAre(""));
}
-// Test for SplitString
TEST(StringUtilTest, SplitString) {
std::vector<std::wstring> r;
diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc
index cee124b0c1a..e64b95f7635 100644
--- a/chromium/base/strings/string_util.cc
+++ b/chromium/base/strings/string_util.cc
@@ -32,13 +32,12 @@ using base::string16;
namespace {
-// Force the singleton used by Empty[W]String[16] to be a unique type. This
+// Force the singleton used by EmptyString[16] to be a unique type. This
// prevents other code that might accidentally use Singleton<string> from
// getting our internal one.
struct EmptyStrings {
EmptyStrings() {}
const std::string s;
- const std::wstring ws;
const string16 s16;
static EmptyStrings* GetInstance() {
@@ -108,17 +107,13 @@ const std::string& EmptyString() {
return EmptyStrings::GetInstance()->s;
}
-const std::wstring& EmptyWString() {
- return EmptyStrings::GetInstance()->ws;
-}
-
const string16& EmptyString16() {
return EmptyStrings::GetInstance()->s16;
}
template<typename STR>
bool ReplaceCharsT(const STR& input,
- const typename STR::value_type replace_chars[],
+ const STR& replace_chars,
const STR& replace_with,
STR* output) {
bool removed = false;
@@ -137,41 +132,41 @@ bool ReplaceCharsT(const STR& input,
}
bool ReplaceChars(const string16& input,
- const char16 replace_chars[],
+ const base::StringPiece16& replace_chars,
const string16& replace_with,
string16* output) {
- return ReplaceCharsT(input, replace_chars, replace_with, output);
+ return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
}
bool ReplaceChars(const std::string& input,
- const char replace_chars[],
+ const base::StringPiece& replace_chars,
const std::string& replace_with,
std::string* output) {
- return ReplaceCharsT(input, replace_chars, replace_with, output);
+ return ReplaceCharsT(input, replace_chars.as_string(), replace_with, output);
}
bool RemoveChars(const string16& input,
- const char16 remove_chars[],
+ const base::StringPiece16& remove_chars,
string16* output) {
- return ReplaceChars(input, remove_chars, string16(), output);
+ return ReplaceChars(input, remove_chars.as_string(), string16(), output);
}
bool RemoveChars(const std::string& input,
- const char remove_chars[],
+ const base::StringPiece& remove_chars,
std::string* output) {
- return ReplaceChars(input, remove_chars, std::string(), output);
+ return ReplaceChars(input, remove_chars.as_string(), std::string(), output);
}
template<typename STR>
TrimPositions TrimStringT(const STR& input,
- const typename STR::value_type trim_chars[],
+ const STR& trim_chars,
TrimPositions positions,
STR* output) {
// Find the edges of leading/trailing whitespace as desired.
- const typename STR::size_type last_char = input.length() - 1;
- const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ?
+ const size_t last_char = input.length() - 1;
+ const size_t first_good_char = (positions & TRIM_LEADING) ?
input.find_first_not_of(trim_chars) : 0;
- const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ?
+ const size_t last_good_char = (positions & TRIM_TRAILING) ?
input.find_last_not_of(trim_chars) : last_char;
// When the string was all whitespace, report that we stripped off whitespace
@@ -195,15 +190,17 @@ TrimPositions TrimStringT(const STR& input,
}
bool TrimString(const string16& input,
- const char16 trim_chars[],
+ const base::StringPiece16& trim_chars,
string16* output) {
- return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+ return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
+ TRIM_NONE;
}
bool TrimString(const std::string& input,
- const char trim_chars[],
+ const base::StringPiece& trim_chars,
std::string* output) {
- return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+ return TrimStringT(input, trim_chars.as_string(), TRIM_ALL, output) !=
+ TRIM_NONE;
}
void TruncateUTF8ToByteSize(const std::string& input,
@@ -242,18 +239,17 @@ void TruncateUTF8ToByteSize(const std::string& input,
output->clear();
}
-} // namespace base
-
-TrimPositions TrimWhitespace(const base::string16& input,
+TrimPositions TrimWhitespace(const string16& input,
TrimPositions positions,
- base::string16* output) {
- return base::TrimStringT(input, base::kWhitespaceUTF16, positions, output);
+ string16* output) {
+ return TrimStringT(input, base::string16(kWhitespaceUTF16), positions,
+ output);
}
TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output) {
- return base::TrimStringT(input, base::kWhitespaceASCII, positions, output);
+ return TrimStringT(input, std::string(kWhitespaceASCII), positions, output);
}
// This function is only for backward-compatibility.
@@ -316,49 +312,14 @@ std::string CollapseWhitespaceASCII(const std::string& text,
return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
}
-bool ContainsOnlyWhitespaceASCII(const std::string& str) {
- for (std::string::const_iterator i(str.begin()); i != str.end(); ++i) {
- if (!IsAsciiWhitespace(*i))
- return false;
- }
- return true;
-}
-
-bool ContainsOnlyWhitespace(const base::string16& str) {
- return str.find_first_not_of(base::kWhitespaceUTF16) == string16::npos;
-}
-
-template<typename STR>
-static bool ContainsOnlyCharsT(const STR& input, const STR& characters) {
- for (typename STR::const_iterator iter = input.begin();
- iter != input.end(); ++iter) {
- if (characters.find(*iter) == STR::npos)
- return false;
- }
- return true;
-}
-
-bool ContainsOnlyChars(const string16& input, const string16& characters) {
- return ContainsOnlyCharsT(input, characters);
-}
-
-bool ContainsOnlyChars(const std::string& input,
- const std::string& characters) {
- return ContainsOnlyCharsT(input, characters);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
-bool IsStringASCII(const std::wstring& str);
-#endif
-
-std::string WideToASCII(const std::wstring& wide) {
- DCHECK(IsStringASCII(wide)) << wide;
- return std::string(wide.begin(), wide.end());
+bool ContainsOnlyChars(const StringPiece& input,
+ const StringPiece& characters) {
+ return input.find_first_not_of(characters) == StringPiece::npos;
}
-std::string UTF16ToASCII(const string16& utf16) {
- DCHECK(IsStringASCII(utf16)) << utf16;
- return std::string(utf16.begin(), utf16.end());
+bool ContainsOnlyChars(const StringPiece16& input,
+ const StringPiece16& characters) {
+ return input.find_first_not_of(characters) == StringPiece16::npos;
}
template<class STR>
@@ -371,20 +332,14 @@ static bool DoIsStringASCII(const STR& str) {
return true;
}
-#if !defined(WCHAR_T_IS_UTF16)
-bool IsStringASCII(const std::wstring& str) {
+bool IsStringASCII(const StringPiece& str) {
return DoIsStringASCII(str);
}
-#endif
bool IsStringASCII(const string16& str) {
return DoIsStringASCII(str);
}
-bool IsStringASCII(const base::StringPiece& str) {
- return DoIsStringASCII(str);
-}
-
bool IsStringUTF8(const std::string& str) {
const char *src = str.data();
int32 src_len = static_cast<int32>(str.length());
@@ -393,12 +348,14 @@ bool IsStringUTF8(const std::string& str) {
while (char_index < src_len) {
int32 code_point;
CBU8_NEXT(src, char_index, src_len, code_point);
- if (!base::IsValidCharacter(code_point))
+ if (!IsValidCharacter(code_point))
return false;
}
return true;
}
+} // namespace base
+
template<typename Iter>
static inline bool DoLowerCaseEqualsASCII(Iter a_begin,
Iter a_end,
@@ -481,17 +438,15 @@ bool StartsWith(const string16& str, const string16& search,
template <typename STR>
bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) {
- typename STR::size_type str_length = str.length();
- typename STR::size_type search_length = search.length();
+ size_t str_length = str.length();
+ size_t search_length = search.length();
if (search_length > str_length)
return false;
- if (case_sensitive) {
+ if (case_sensitive)
return str.compare(str_length - search_length, search_length, search) == 0;
- } else {
- return std::equal(search.begin(), search.end(),
- str.begin() + (str_length - search_length),
- base::CaseInsensitiveCompare<typename STR::value_type>());
- }
+ return std::equal(search.begin(), search.end(),
+ str.begin() + (str_length - search_length),
+ base::CaseInsensitiveCompare<typename STR::value_type>());
}
bool EndsWith(const std::string& str, const std::string& search,
@@ -532,12 +487,12 @@ string16 FormatBytesUnlocalized(int64 bytes) {
kByteStringsUnlocalized[dimension]);
}
- return ASCIIToUTF16(buf);
+ return base::ASCIIToUTF16(buf);
}
template<class StringType>
void DoReplaceSubstringsAfterOffset(StringType* str,
- typename StringType::size_type start_offset,
+ size_t start_offset,
const StringType& find_this,
const StringType& replace_with,
bool replace_all) {
@@ -545,7 +500,7 @@ void DoReplaceSubstringsAfterOffset(StringType* str,
return;
DCHECK(!find_this.empty());
- for (typename StringType::size_type offs(str->find(find_this, start_offset));
+ for (size_t offs(str->find(find_this, start_offset));
offs != StringType::npos; offs = str->find(find_this, offs)) {
str->replace(offs, find_this.length(), replace_with);
offs += replace_with.length();
@@ -556,7 +511,7 @@ void DoReplaceSubstringsAfterOffset(StringType* str,
}
void ReplaceFirstSubstringAfterOffset(string16* str,
- string16::size_type start_offset,
+ size_t start_offset,
const string16& find_this,
const string16& replace_with) {
DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
@@ -564,7 +519,7 @@ void ReplaceFirstSubstringAfterOffset(string16* str,
}
void ReplaceFirstSubstringAfterOffset(std::string* str,
- std::string::size_type start_offset,
+ size_t start_offset,
const std::string& find_this,
const std::string& replace_with) {
DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
@@ -572,7 +527,7 @@ void ReplaceFirstSubstringAfterOffset(std::string* str,
}
void ReplaceSubstringsAfterOffset(string16* str,
- string16::size_type start_offset,
+ size_t start_offset,
const string16& find_this,
const string16& replace_with) {
DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
@@ -580,7 +535,7 @@ void ReplaceSubstringsAfterOffset(string16* str,
}
void ReplaceSubstringsAfterOffset(std::string* str,
- std::string::size_type start_offset,
+ size_t start_offset,
const std::string& find_this,
const std::string& replace_with) {
DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with,
@@ -594,9 +549,9 @@ static size_t TokenizeT(const STR& str,
std::vector<STR>* tokens) {
tokens->clear();
- typename STR::size_type start = str.find_first_not_of(delimiters);
+ size_t start = str.find_first_not_of(delimiters);
while (start != STR::npos) {
- typename STR::size_type end = str.find_first_of(delimiters, start + 1);
+ size_t end = str.find_first_of(delimiters, start + 1);
if (end == STR::npos) {
tokens->push_back(str.substr(start));
break;
diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h
index 7b4b2193abf..a573e227df9 100644
--- a/chromium/base/strings/string_util.h
+++ b/chromium/base/strings/string_util.h
@@ -19,8 +19,6 @@
#include "base/strings/string16.h"
#include "base/strings/string_piece.h" // For implicit conversions.
-// Safe standard library wrappers for all platforms.
-
namespace base {
// C standard-library functions like "strncasecmp" and "snprintf" that aren't
@@ -152,10 +150,10 @@ BASE_EXPORT extern const char kUtf8ByteOrderMark[];
// if any characters were removed. |remove_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool RemoveChars(const string16& input,
- const char16 remove_chars[],
+ const base::StringPiece16& remove_chars,
string16* output);
BASE_EXPORT bool RemoveChars(const std::string& input,
- const char remove_chars[],
+ const base::StringPiece& remove_chars,
std::string* output);
// Replaces characters in |replace_chars| from anywhere in |input| with
@@ -164,11 +162,11 @@ BASE_EXPORT bool RemoveChars(const std::string& input,
// |replace_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool ReplaceChars(const string16& input,
- const char16 replace_chars[],
+ const base::StringPiece16& replace_chars,
const string16& replace_with,
string16* output);
BASE_EXPORT bool ReplaceChars(const std::string& input,
- const char replace_chars[],
+ const base::StringPiece& replace_chars,
const std::string& replace_with,
std::string* output);
@@ -176,10 +174,10 @@ BASE_EXPORT bool ReplaceChars(const std::string& input,
// |trim_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
BASE_EXPORT bool TrimString(const string16& input,
- const char16 trim_chars[],
+ const base::StringPiece16& trim_chars,
string16* output);
BASE_EXPORT bool TrimString(const std::string& input,
- const char trim_chars[],
+ const base::StringPiece& trim_chars,
std::string* output);
// Truncates a string to the nearest UTF-8 character that will leave
@@ -188,16 +186,6 @@ BASE_EXPORT void TruncateUTF8ToByteSize(const std::string& input,
const size_t byte_size,
std::string* output);
-} // namespace base
-
-#if defined(OS_WIN)
-#include "base/strings/string_util_win.h"
-#elif defined(OS_POSIX)
-#include "base/strings/string_util_posix.h"
-#else
-#error Define string operations appropriately for your platform
-#endif
-
// Trims any whitespace from either end of the input string. Returns where
// whitespace was found.
// The non-wide version has two functions:
@@ -211,7 +199,7 @@ enum TrimPositions {
TRIM_TRAILING = 1 << 1,
TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
};
-BASE_EXPORT TrimPositions TrimWhitespace(const base::string16& input,
+BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
TrimPositions positions,
base::string16* output);
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
@@ -232,29 +220,19 @@ BASE_EXPORT TrimPositions TrimWhitespace(const std::string& input,
// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
// sequences containing a CR or LF are trimmed.
// (3) All other whitespace sequences are converted to single spaces.
-BASE_EXPORT base::string16 CollapseWhitespace(
- const base::string16& text,
+BASE_EXPORT string16 CollapseWhitespace(
+ const string16& text,
bool trim_sequences_with_line_breaks);
BASE_EXPORT std::string CollapseWhitespaceASCII(
const std::string& text,
bool trim_sequences_with_line_breaks);
-// Returns true if the passed string is empty or contains only white-space
-// characters.
-BASE_EXPORT bool ContainsOnlyWhitespaceASCII(const std::string& str);
-BASE_EXPORT bool ContainsOnlyWhitespace(const base::string16& str);
-
// Returns true if |input| is empty or contains only characters found in
// |characters|.
-BASE_EXPORT bool ContainsOnlyChars(const base::string16& input,
- const base::string16& characters);
-BASE_EXPORT bool ContainsOnlyChars(const std::string& input,
- const std::string& characters);
-
-// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
-// beforehand.
-BASE_EXPORT std::string WideToASCII(const std::wstring& wide);
-BASE_EXPORT std::string UTF16ToASCII(const base::string16& utf16);
+BASE_EXPORT bool ContainsOnlyChars(const StringPiece& input,
+ const StringPiece& characters);
+BASE_EXPORT bool ContainsOnlyChars(const StringPiece16& input,
+ const StringPiece16& characters);
// Returns true if the specified string matches the criteria. How can a wide
// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
@@ -268,8 +246,18 @@ BASE_EXPORT std::string UTF16ToASCII(const base::string16& utf16);
// there's a use case for just checking the structural validity, we have to
// add a new function for that.
BASE_EXPORT bool IsStringUTF8(const std::string& str);
-BASE_EXPORT bool IsStringASCII(const base::StringPiece& str);
-BASE_EXPORT bool IsStringASCII(const base::string16& str);
+BASE_EXPORT bool IsStringASCII(const StringPiece& str);
+BASE_EXPORT bool IsStringASCII(const string16& str);
+
+} // namespace base
+
+#if defined(OS_WIN)
+#include "base/strings/string_util_win.h"
+#elif defined(OS_POSIX)
+#include "base/strings/string_util_posix.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
// Converts the elements of the given string. This version uses a pointer to
// clearly differentiate it from the non-pointer variant.
@@ -390,12 +378,12 @@ BASE_EXPORT base::string16 FormatBytesUnlocalized(int64 bytes);
// |find_this| with |replace_with|.
BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
base::string16* str,
- base::string16::size_type start_offset,
+ size_t start_offset,
const base::string16& find_this,
const base::string16& replace_with);
BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
std::string* str,
- std::string::size_type start_offset,
+ size_t start_offset,
const std::string& find_this,
const std::string& replace_with);
@@ -407,14 +395,13 @@ BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
// std::replace(str.begin(), str.end(), 'a', 'b');
BASE_EXPORT void ReplaceSubstringsAfterOffset(
base::string16* str,
- base::string16::size_type start_offset,
+ size_t start_offset,
const base::string16& find_this,
const base::string16& replace_with);
-BASE_EXPORT void ReplaceSubstringsAfterOffset(
- std::string* str,
- std::string::size_type start_offset,
- const std::string& find_this,
- const std::string& replace_with);
+BASE_EXPORT void ReplaceSubstringsAfterOffset(std::string* str,
+ size_t start_offset,
+ const std::string& find_this,
+ const std::string& replace_with);
// Reserves enough memory in |str| to accommodate |length_with_null| characters,
// sets the size of |str| to |length_with_null - 1| characters, and returns a
diff --git a/chromium/base/strings/string_util_constants.cc b/chromium/base/strings/string_util_constants.cc
index 2a28a2b5126..146e5fd5355 100644
--- a/chromium/base/strings/string_util_constants.cc
+++ b/chromium/base/strings/string_util_constants.cc
@@ -7,33 +7,31 @@
namespace base {
#define WHITESPACE_UNICODE \
- 0x0009, /* <control-0009> to <control-000D> */ \
- 0x000A, \
- 0x000B, \
- 0x000C, \
- 0x000D, \
- 0x0020, /* Space */ \
- 0x0085, /* <control-0085> */ \
- 0x00A0, /* No-Break Space */ \
- 0x1680, /* Ogham Space Mark */ \
- 0x180E, /* Mongolian Vowel Separator */ \
- 0x2000, /* En Quad to Hair Space */ \
- 0x2001, \
- 0x2002, \
- 0x2003, \
- 0x2004, \
- 0x2005, \
- 0x2006, \
- 0x2007, \
- 0x2008, \
- 0x2009, \
- 0x200A, \
- 0x200C, /* Zero Width Non-Joiner */ \
- 0x2028, /* Line Separator */ \
- 0x2029, /* Paragraph Separator */ \
- 0x202F, /* Narrow No-Break Space */ \
- 0x205F, /* Medium Mathematical Space */ \
- 0x3000, /* Ideographic Space */ \
+ 0x0009, /* CHARACTER TABULATION */ \
+ 0x000A, /* LINE FEED (LF) */ \
+ 0x000B, /* LINE TABULATION */ \
+ 0x000C, /* FORM FEED (FF) */ \
+ 0x000D, /* CARRIAGE RETURN (CR) */ \
+ 0x0020, /* SPACE */ \
+ 0x0085, /* NEXT LINE (NEL) */ \
+ 0x00A0, /* NO-BREAK SPACE */ \
+ 0x1680, /* OGHAM SPACE MARK */ \
+ 0x2000, /* EN QUAD */ \
+ 0x2001, /* EM QUAD */ \
+ 0x2002, /* EN SPACE */ \
+ 0x2003, /* EM SPACE */ \
+ 0x2004, /* THREE-PER-EM SPACE */ \
+ 0x2005, /* FOUR-PER-EM SPACE */ \
+ 0x2006, /* SIX-PER-EM SPACE */ \
+ 0x2007, /* FIGURE SPACE */ \
+ 0x2008, /* PUNCTUATION SPACE */ \
+ 0x2009, /* THIN SPACE */ \
+ 0x200A, /* HAIR SPACE */ \
+ 0x2028, /* LINE SEPARATOR */ \
+ 0x2029, /* PARAGRAPH SEPARATOR */ \
+ 0x202F, /* NARROW NO-BREAK SPACE */ \
+ 0x205F, /* MEDIUM MATHEMATICAL SPACE */ \
+ 0x3000, /* IDEOGRAPHIC SPACE */ \
0
const wchar_t kWhitespaceWide[] = {
@@ -45,12 +43,12 @@ const char16 kWhitespaceUTF16[] = {
};
const char kWhitespaceASCII[] = {
- 0x09, // <control-0009> to <control-000D>
- 0x0A,
- 0x0B,
- 0x0C,
- 0x0D,
- 0x20, // Space
+ 0x09, // CHARACTER TABULATION
+ 0x0A, // LINE FEED (LF)
+ 0x0B, // LINE TABULATION
+ 0x0C, // FORM FEED (FF)
+ 0x0D, // CARRIAGE RETURN (CR)
+ 0x20, // SPACE
0
};
diff --git a/chromium/base/strings/string_util_posix.h b/chromium/base/strings/string_util_posix.h
index 34b14f178f8..f4009d4ae7f 100644
--- a/chromium/base/strings/string_util_posix.h
+++ b/chromium/base/strings/string_util_posix.h
@@ -11,7 +11,6 @@
#include <wchar.h>
#include "base/logging.h"
-#include "base/strings/string_util.h"
namespace base {
diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc
index e743bb41223..4ea9069a255 100644
--- a/chromium/base/strings/string_util_unittest.cc
+++ b/chromium/base/strings/string_util_unittest.cc
@@ -319,24 +319,6 @@ TEST(StringUtilTest, CollapseWhitespaceASCII) {
}
}
-TEST(StringUtilTest, ContainsOnlyWhitespaceASCII) {
- EXPECT_TRUE(ContainsOnlyWhitespaceASCII(std::string()));
- EXPECT_TRUE(ContainsOnlyWhitespaceASCII(" "));
- EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t"));
- EXPECT_TRUE(ContainsOnlyWhitespaceASCII("\t \r \n "));
- EXPECT_FALSE(ContainsOnlyWhitespaceASCII("a"));
- EXPECT_FALSE(ContainsOnlyWhitespaceASCII("\thello\r \n "));
-}
-
-TEST(StringUtilTest, ContainsOnlyWhitespace) {
- EXPECT_TRUE(ContainsOnlyWhitespace(string16()));
- EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16(" ")));
- EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t")));
- EXPECT_TRUE(ContainsOnlyWhitespace(ASCIIToUTF16("\t \r \n ")));
- EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("a")));
- EXPECT_FALSE(ContainsOnlyWhitespace(ASCIIToUTF16("\thello\r \n ")));
-}
-
TEST(StringUtilTest, IsStringUTF8) {
EXPECT_TRUE(IsStringUTF8("abc"));
EXPECT_TRUE(IsStringUTF8("\xc2\x81"));
@@ -418,20 +400,20 @@ TEST(StringUtilTest, ConvertASCII) {
for (size_t i = 0; i < arraysize(char_cases); ++i) {
EXPECT_TRUE(IsStringASCII(char_cases[i]));
- std::wstring wide = ASCIIToWide(char_cases[i]);
- EXPECT_EQ(wchar_cases[i], wide);
+ string16 utf16 = ASCIIToUTF16(char_cases[i]);
+ EXPECT_EQ(WideToUTF16(wchar_cases[i]), utf16);
- std::string ascii = WideToASCII(wchar_cases[i]);
+ std::string ascii = UTF16ToASCII(WideToUTF16(wchar_cases[i]));
EXPECT_EQ(char_cases[i], ascii);
}
EXPECT_FALSE(IsStringASCII("Google \x80Video"));
// Convert empty strings.
- std::wstring wempty;
+ string16 empty16;
std::string empty;
- EXPECT_EQ(empty, WideToASCII(wempty));
- EXPECT_EQ(wempty, ASCIIToWide(empty));
+ EXPECT_EQ(empty, UTF16ToASCII(empty16));
+ EXPECT_EQ(empty16, ASCIIToUTF16(empty));
// Convert strings with an embedded NUL character.
const char chars_with_nul[] = "test\0string";
@@ -440,7 +422,7 @@ TEST(StringUtilTest, ConvertASCII) {
std::wstring wide_with_nul = ASCIIToWide(string_with_nul);
EXPECT_EQ(static_cast<std::wstring::size_type>(length_with_nul),
wide_with_nul.length());
- std::string narrow_with_nul = WideToASCII(wide_with_nul);
+ std::string narrow_with_nul = UTF16ToASCII(WideToUTF16(wide_with_nul));
EXPECT_EQ(static_cast<std::string::size_type>(length_with_nul),
narrow_with_nul.length());
EXPECT_EQ(0, string_with_nul.compare(narrow_with_nul));
@@ -1029,13 +1011,8 @@ TEST(StringUtilTest, LcpyTest) {
EXPECT_EQ(1, dst[0]);
EXPECT_EQ(2, dst[1]);
EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", 0));
-#if defined(WCHAR_T_IS_UNSIGNED)
- EXPECT_EQ(1U, wdst[0]);
- EXPECT_EQ(2U, wdst[1]);
-#else
- EXPECT_EQ(1, wdst[0]);
- EXPECT_EQ(2, wdst[1]);
-#endif
+ EXPECT_EQ(static_cast<wchar_t>(1), wdst[0]);
+ EXPECT_EQ(static_cast<wchar_t>(2), wdst[1]);
}
// Test the case were we _just_ competely fit including the null.
@@ -1161,6 +1138,21 @@ TEST(StringUtilTest, ContainsOnlyChars) {
EXPECT_TRUE(ContainsOnlyChars("1", "4321"));
EXPECT_TRUE(ContainsOnlyChars("123", "4321"));
EXPECT_FALSE(ContainsOnlyChars("123a", "4321"));
+
+ EXPECT_TRUE(ContainsOnlyChars(std::string(), kWhitespaceASCII));
+ EXPECT_TRUE(ContainsOnlyChars(" ", kWhitespaceASCII));
+ EXPECT_TRUE(ContainsOnlyChars("\t", kWhitespaceASCII));
+ EXPECT_TRUE(ContainsOnlyChars("\t \r \n ", kWhitespaceASCII));
+ EXPECT_FALSE(ContainsOnlyChars("a", kWhitespaceASCII));
+ EXPECT_FALSE(ContainsOnlyChars("\thello\r \n ", kWhitespaceASCII));
+
+ EXPECT_TRUE(ContainsOnlyChars(string16(), kWhitespaceUTF16));
+ EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16(" "), kWhitespaceUTF16));
+ EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16("\t"), kWhitespaceUTF16));
+ EXPECT_TRUE(ContainsOnlyChars(ASCIIToUTF16("\t \r \n "), kWhitespaceUTF16));
+ EXPECT_FALSE(ContainsOnlyChars(ASCIIToUTF16("a"), kWhitespaceUTF16));
+ EXPECT_FALSE(ContainsOnlyChars(ASCIIToUTF16("\thello\r \n "),
+ kWhitespaceUTF16));
}
class WriteIntoTest : public testing::Test {
diff --git a/chromium/base/strings/stringprintf.cc b/chromium/base/strings/stringprintf.cc
index fe23daaa355..3d024fa65aa 100644
--- a/chromium/base/strings/stringprintf.cc
+++ b/chromium/base/strings/stringprintf.cc
@@ -66,19 +66,17 @@ static void StringAppendVT(StringType* dst,
int mem_length = arraysize(stack_buf);
while (true) {
if (result < 0) {
-#if !defined(OS_WIN)
+#if defined(OS_WIN)
// On Windows, vsnprintfT always returns the number of characters in a
// fully-formatted string, so if we reach this point, something else is
// wrong and no amount of buffer-doubling is going to fix it.
+ return;
+#else
if (errno != 0 && errno != EOVERFLOW)
-#endif
- {
- // If an error other than overflow occurred, it's never going to work.
- DLOG(WARNING) << "Unable to printf the requested string due to error.";
return;
- }
// Try doubling the buffer size.
mem_length *= 2;
+#endif
} else {
// We need exactly "result + 1" characters.
mem_length = result + 1;
diff --git a/chromium/base/strings/utf_offset_string_conversions.cc b/chromium/base/strings/utf_offset_string_conversions.cc
index 339bd5e1a75..c2270bfce2f 100644
--- a/chromium/base/strings/utf_offset_string_conversions.cc
+++ b/chromium/base/strings/utf_offset_string_conversions.cc
@@ -6,30 +6,188 @@
#include <algorithm>
+#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversion_utils.h"
namespace base {
+OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length)
+ : original_offset(original_offset),
+ original_length(original_length),
+ output_length(output_length) {
+}
+
+// static
+void OffsetAdjuster::AdjustOffsets(
+ const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_adjustment) {
+ if (!offsets_for_adjustment || adjustments.empty())
+ return;
+ for (std::vector<size_t>::iterator i(offsets_for_adjustment->begin());
+ i != offsets_for_adjustment->end(); ++i)
+ AdjustOffset(adjustments, &(*i));
+}
+
+// static
+void OffsetAdjuster::AdjustOffset(const Adjustments& adjustments,
+ size_t* offset) {
+ if (*offset == string16::npos)
+ return;
+ int adjustment = 0;
+ for (Adjustments::const_iterator i = adjustments.begin();
+ i != adjustments.end(); ++i) {
+ if (*offset <= i->original_offset)
+ break;
+ if (*offset < (i->original_offset + i->original_length)) {
+ *offset = string16::npos;
+ return;
+ }
+ adjustment += static_cast<int>(i->original_length - i->output_length);
+ }
+ *offset -= adjustment;
+}
+
+// static
+void OffsetAdjuster::UnadjustOffsets(
+ const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_unadjustment) {
+ if (!offsets_for_unadjustment || adjustments.empty())
+ return;
+ for (std::vector<size_t>::iterator i(offsets_for_unadjustment->begin());
+ i != offsets_for_unadjustment->end(); ++i)
+ UnadjustOffset(adjustments, &(*i));
+}
+
+// static
+void OffsetAdjuster::UnadjustOffset(const Adjustments& adjustments,
+ size_t* offset) {
+ if (*offset == string16::npos)
+ return;
+ int adjustment = 0;
+ for (Adjustments::const_iterator i = adjustments.begin();
+ i != adjustments.end(); ++i) {
+ if (*offset + adjustment <= i->original_offset)
+ break;
+ adjustment += static_cast<int>(i->original_length - i->output_length);
+ if ((*offset + adjustment) <
+ (i->original_offset + i->original_length)) {
+ *offset = string16::npos;
+ return;
+ }
+ }
+ *offset += adjustment;
+}
+
+// static
+void OffsetAdjuster::MergeSequentialAdjustments(
+ const Adjustments& first_adjustments,
+ Adjustments* adjustments_on_adjusted_string) {
+ Adjustments::iterator adjusted_iter = adjustments_on_adjusted_string->begin();
+ Adjustments::const_iterator first_iter = first_adjustments.begin();
+ // Simultaneously iterate over all |adjustments_on_adjusted_string| and
+ // |first_adjustments|, adding adjustments to or correcting the adjustments
+ // in |adjustments_on_adjusted_string| as we go. |shift| keeps track of the
+ // current number of characters collapsed by |first_adjustments| up to this
+ // point. |currently_collapsing| keeps track of the number of characters
+ // collapsed by |first_adjustments| into the current |adjusted_iter|'s
+ // length. These are characters that will change |shift| as soon as we're
+ // done processing the current |adjusted_iter|; they are not yet reflected in
+ // |shift|.
+ size_t shift = 0;
+ size_t currently_collapsing = 0;
+ while (adjusted_iter != adjustments_on_adjusted_string->end()) {
+ if ((first_iter == first_adjustments.end()) ||
+ ((adjusted_iter->original_offset + shift +
+ adjusted_iter->original_length) <= first_iter->original_offset)) {
+ // Entire |adjusted_iter| (accounting for its shift and including its
+ // whole original length) comes before |first_iter|.
+ //
+ // Correct the offset at |adjusted_iter| and move onto the next
+ // adjustment that needs revising.
+ adjusted_iter->original_offset += shift;
+ shift += currently_collapsing;
+ currently_collapsing = 0;
+ ++adjusted_iter;
+ } else if ((adjusted_iter->original_offset + shift) >
+ first_iter->original_offset) {
+ // |first_iter| comes before the |adjusted_iter| (as adjusted by |shift|).
+
+ // It's not possible for the adjustments to overlap. (It shouldn't
+ // be possible that we have an |adjusted_iter->original_offset| that,
+ // when adjusted by the computed |shift|, is in the middle of
+ // |first_iter|'s output's length. After all, that would mean the
+ // current adjustment_on_adjusted_string somehow points to an offset
+ // that was supposed to have been eliminated by the first set of
+ // adjustments.)
+ DCHECK_LE(first_iter->original_offset + first_iter->output_length,
+ adjusted_iter->original_offset + shift);
+
+ // Add the |first_adjustment_iter| to the full set of adjustments while
+ // making sure |adjusted_iter| continues pointing to the same element.
+ // We do this by inserting the |first_adjustment_iter| right before
+ // |adjusted_iter|, then incrementing |adjusted_iter| so it points to
+ // the following element.
+ shift += first_iter->original_length - first_iter->output_length;
+ adjusted_iter = adjustments_on_adjusted_string->insert(
+ adjusted_iter, *first_iter);
+ ++adjusted_iter;
+ ++first_iter;
+ } else {
+ // The first adjustment adjusted something that then got further adjusted
+ // by the second set of adjustments. In other words, |first_iter| points
+ // to something in the range covered by |adjusted_iter|'s length (after
+ // accounting for |shift|). Precisely,
+ // adjusted_iter->original_offset + shift
+ // <=
+ // first_iter->original_offset
+ // <=
+ // adjusted_iter->original_offset + shift +
+ // adjusted_iter->original_length
+
+ // Modify the current |adjusted_iter| to include whatever collapsing
+ // happened in |first_iter|, then advance to the next |first_adjustments|
+ // because we dealt with the current one.
+ const int collapse = static_cast<int>(first_iter->original_length) -
+ static_cast<int>(first_iter->output_length);
+ // This function does not know how to deal with a string that expands and
+ // then gets modified, only strings that collapse and then get modified.
+ DCHECK_GT(collapse, 0);
+ adjusted_iter->original_length += collapse;
+ currently_collapsing += collapse;
+ ++first_iter;
+ }
+ }
+ DCHECK_EQ(0u, currently_collapsing);
+ if (first_iter != first_adjustments.end()) {
+ // Only first adjustments are left. These do not need to be modified.
+ // (Their offsets are already correct with respect to the original string.)
+ // Append them all.
+ DCHECK(adjusted_iter == adjustments_on_adjusted_string->end());
+ adjustments_on_adjusted_string->insert(
+ adjustments_on_adjusted_string->end(), first_iter,
+ first_adjustments.end());
+ }
+}
+
// Converts the given source Unicode character type to the given destination
// Unicode character type as a STL string. The given input buffer and size
// determine the source, and the given output STL string will be replaced by
-// the result.
+// the result. If non-NULL, |adjustments| is set to reflect the all the
+// alterations to the string that are not one-character-to-one-character.
+// It will always be sorted by increasing offset.
template<typename SrcChar, typename DestStdString>
bool ConvertUnicode(const SrcChar* src,
size_t src_len,
DestStdString* output,
- std::vector<size_t>* offsets_for_adjustment) {
- if (offsets_for_adjustment) {
- std::for_each(offsets_for_adjustment->begin(),
- offsets_for_adjustment->end(),
- LimitOffset<DestStdString>(src_len));
- }
-
+ OffsetAdjuster::Adjustments* adjustments) {
+ if (adjustments)
+ adjustments->clear();
// ICU requires 32-bit numbers.
bool success = true;
- OffsetAdjuster offset_adjuster(offsets_for_adjustment);
int32 src_len32 = static_cast<int32>(src_len);
for (int32 i = 0; i < src_len32; i++) {
uint32 code_point;
@@ -41,122 +199,62 @@ bool ConvertUnicode(const SrcChar* src,
chars_written = WriteUnicodeCharacter(0xFFFD, output);
success = false;
}
- if (offsets_for_adjustment) {
- // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
- // character read, not after it (so that incrementing it in the loop
- // increment will place it at the right location), so we need to account
- // for that in determining the amount that was read.
- offset_adjuster.Add(OffsetAdjuster::Adjustment(original_i,
- i - original_i + 1, chars_written));
+
+ // Only bother writing an adjustment if this modification changed the
+ // length of this character.
+ // NOTE: ReadUnicodeCharacter() adjusts |i| to point _at_ the last
+ // character read, not after it (so that incrementing it in the loop
+ // increment will place it at the right location), so we need to account
+ // for that in determining the amount that was read.
+ if (adjustments && ((i - original_i + 1) != chars_written)) {
+ adjustments->push_back(OffsetAdjuster::Adjustment(
+ original_i, i - original_i + 1, chars_written));
}
}
return success;
}
-bool UTF8ToUTF16AndAdjustOffset(const char* src,
- size_t src_len,
- string16* output,
- size_t* offset_for_adjustment) {
- std::vector<size_t> offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
+bool UTF8ToUTF16WithAdjustments(
+ const char* src,
+ size_t src_len,
+ string16* output,
+ base::OffsetAdjuster::Adjustments* adjustments) {
PrepareForUTF16Or32Output(src, src_len, output);
- bool ret = ConvertUnicode(src, src_len, output, &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
- return ret;
+ return ConvertUnicode(src, src_len, output, adjustments);
}
-bool UTF8ToUTF16AndAdjustOffsets(const char* src,
- size_t src_len,
- string16* output,
- std::vector<size_t>* offsets_for_adjustment) {
- PrepareForUTF16Or32Output(src, src_len, output);
- return ConvertUnicode(src, src_len, output, offsets_for_adjustment);
-}
-
-string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8,
- size_t* offset_for_adjustment) {
- std::vector<size_t> offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
+string16 UTF8ToUTF16WithAdjustments(
+ const base::StringPiece& utf8,
+ base::OffsetAdjuster::Adjustments* adjustments) {
string16 result;
- UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result,
- &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
+ UTF8ToUTF16WithAdjustments(utf8.data(), utf8.length(), &result, adjustments);
return result;
}
string16 UTF8ToUTF16AndAdjustOffsets(
const base::StringPiece& utf8,
std::vector<size_t>* offsets_for_adjustment) {
- string16 result;
- UTF8ToUTF16AndAdjustOffsets(utf8.data(), utf8.length(), &result,
- offsets_for_adjustment);
- return result;
-}
-
-std::string UTF16ToUTF8AndAdjustOffset(
- const base::StringPiece16& utf16,
- size_t* offset_for_adjustment) {
- std::vector<size_t> offsets;
- if (offset_for_adjustment)
- offsets.push_back(*offset_for_adjustment);
- std::string result = UTF16ToUTF8AndAdjustOffsets(utf16, &offsets);
- if (offset_for_adjustment)
- *offset_for_adjustment = offsets[0];
+ std::for_each(offsets_for_adjustment->begin(),
+ offsets_for_adjustment->end(),
+ LimitOffset<base::StringPiece>(utf8.length()));
+ OffsetAdjuster::Adjustments adjustments;
+ string16 result = UTF8ToUTF16WithAdjustments(utf8, &adjustments);
+ OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
return result;
}
std::string UTF16ToUTF8AndAdjustOffsets(
const base::StringPiece16& utf16,
std::vector<size_t>* offsets_for_adjustment) {
+ std::for_each(offsets_for_adjustment->begin(),
+ offsets_for_adjustment->end(),
+ LimitOffset<base::StringPiece16>(utf16.length()));
std::string result;
PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
- ConvertUnicode(utf16.data(), utf16.length(), &result, offsets_for_adjustment);
+ OffsetAdjuster::Adjustments adjustments;
+ ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments);
+ OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
return result;
}
-OffsetAdjuster::Adjustment::Adjustment(size_t original_offset,
- size_t original_length,
- size_t output_length)
- : original_offset(original_offset),
- original_length(original_length),
- output_length(output_length) {
-}
-
-OffsetAdjuster::OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment)
- : offsets_for_adjustment_(offsets_for_adjustment) {
-}
-
-OffsetAdjuster::~OffsetAdjuster() {
- if (!offsets_for_adjustment_ || adjustments_.empty())
- return;
- for (std::vector<size_t>::iterator i(offsets_for_adjustment_->begin());
- i != offsets_for_adjustment_->end(); ++i)
- AdjustOffset(i);
-}
-
-void OffsetAdjuster::Add(const Adjustment& adjustment) {
- adjustments_.push_back(adjustment);
-}
-
-void OffsetAdjuster::AdjustOffset(std::vector<size_t>::iterator offset) {
- if (*offset == string16::npos)
- return;
- size_t adjustment = 0;
- for (std::vector<Adjustment>::const_iterator i = adjustments_.begin();
- i != adjustments_.end(); ++i) {
- if (*offset <= i->original_offset)
- break;
- if (*offset < (i->original_offset + i->original_length)) {
- *offset = string16::npos;
- return;
- }
- adjustment += (i->original_length - i->output_length);
- }
- *offset -= adjustment;
-}
-
} // namespace base
diff --git a/chromium/base/strings/utf_offset_string_conversions.h b/chromium/base/strings/utf_offset_string_conversions.h
index bdb7c111203..d4494894efb 100644
--- a/chromium/base/strings/utf_offset_string_conversions.h
+++ b/chromium/base/strings/utf_offset_string_conversions.h
@@ -14,35 +14,92 @@
namespace base {
-// Like the conversions in utf_string_conversions.h, but also takes one or more
-// |offset[s]_for_adjustment| representing insertion/selection points between
-// characters: if |src| is "abcd", then 0 is before 'a', 2 is between 'b' and
-// 'c', and 4 is at the end of the string. Valid input offsets range from 0 to
-// |src_len|. On exit, each offset will have been modified to point at the same
-// logical position in the output string. If an offset cannot be successfully
-// adjusted (e.g. because it points into the middle of a multibyte sequence), it
-// will be set to string16::npos.
-//
-// |offset[s]_for_adjustment| may be NULL.
-BASE_EXPORT bool UTF8ToUTF16AndAdjustOffset(const char* src,
- size_t src_len,
- string16* output,
- size_t* offset_for_adjustment);
-BASE_EXPORT bool UTF8ToUTF16AndAdjustOffsets(
+// A helper class and associated data structures to adjust offsets into a
+// string in response to various adjustments one might do to that string
+// (e.g., eliminating a range). For details on offsets, see the comments by
+// the AdjustOffsets() function below.
+class BASE_EXPORT OffsetAdjuster {
+ public:
+ struct BASE_EXPORT Adjustment {
+ Adjustment(size_t original_offset,
+ size_t original_length,
+ size_t output_length);
+
+ size_t original_offset;
+ size_t original_length;
+ size_t output_length;
+ };
+ typedef std::vector<Adjustment> Adjustments;
+
+ // Adjusts all offsets in |offsets_for_adjustment| to reflect the adjustments
+ // recorded in |adjustments|.
+ //
+ // Offsets represents insertion/selection points between characters: if |src|
+ // is "abcd", then 0 is before 'a', 2 is between 'b' and 'c', and 4 is at the
+ // end of the string. Valid input offsets range from 0 to |src_len|. On
+ // exit, each offset will have been modified to point at the same logical
+ // position in the output string. If an offset cannot be successfully
+ // adjusted (e.g., because it points into the middle of a multibyte sequence),
+ // it will be set to string16::npos.
+ static void AdjustOffsets(const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_adjustment);
+
+ // Adjusts the single |offset| to reflect the adjustments recorded in
+ // |adjustments|.
+ static void AdjustOffset(const Adjustments& adjustments,
+ size_t* offset);
+
+ // Adjusts all offsets in |offsets_for_unadjustment| to reflect the reverse
+ // of the adjustments recorded in |adjustments|. In other words, the offsets
+ // provided represent offsets into an adjusted string and the caller wants
+ // to know the offsets they correspond to in the original string. If an
+ // offset cannot be successfully unadjusted (e.g., because it points into
+ // the middle of a multibyte sequence), it will be set to string16::npos.
+ static void UnadjustOffsets(const Adjustments& adjustments,
+ std::vector<size_t>* offsets_for_unadjustment);
+
+ // Adjusts the single |offset| to reflect the reverse of the adjustments
+ // recorded in |adjustments|.
+ static void UnadjustOffset(const Adjustments& adjustments,
+ size_t* offset);
+
+ // Combines two sequential sets of adjustments, storing the combined revised
+ // adjustments in |adjustments_on_adjusted_string|. That is, suppose a
+ // string was altered in some way, with the alterations recorded as
+ // adjustments in |first_adjustments|. Then suppose the resulting string is
+ // further altered, with the alterations recorded as adjustments scored in
+ // |adjustments_on_adjusted_string|, with the offsets recorded in these
+ // adjustments being with respect to the intermediate string. This function
+ // combines the two sets of adjustments into one, storing the result in
+ // |adjustments_on_adjusted_string|, whose offsets are correct with respect
+ // to the original string.
+ //
+ // Assumes both parameters are sorted by increasing offset.
+ //
+ // WARNING: Only supports |first_adjustments| that involve collapsing ranges
+ // of text, not expanding ranges.
+ static void MergeSequentialAdjustments(
+ const Adjustments& first_adjustments,
+ Adjustments* adjustments_on_adjusted_string);
+};
+
+// Like the conversions in utf_string_conversions.h, but also fills in an
+// |adjustments| parameter that reflects the alterations done to the string.
+// It may be NULL.
+BASE_EXPORT bool UTF8ToUTF16WithAdjustments(
const char* src,
size_t src_len,
string16* output,
- std::vector<size_t>* offsets_for_adjustment);
-
-BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffset(const base::StringPiece& utf8,
- size_t* offset_for_adjustment);
+ base::OffsetAdjuster::Adjustments* adjustments);
+BASE_EXPORT string16 UTF8ToUTF16WithAdjustments(
+ const base::StringPiece& utf8,
+ base::OffsetAdjuster::Adjustments* adjustments);
+// As above, but instead internally examines the adjustments and applies them
+// to |offsets_for_adjustment|. See comments by AdjustOffsets().
BASE_EXPORT string16 UTF8ToUTF16AndAdjustOffsets(
const base::StringPiece& utf8,
std::vector<size_t>* offsets_for_adjustment);
-BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffset(
- const base::StringPiece16& utf16,
- size_t* offset_for_adjustment);
BASE_EXPORT std::string UTF16ToUTF8AndAdjustOffsets(
const base::StringPiece16& utf16,
std::vector<size_t>* offsets_for_adjustment);
@@ -64,36 +121,6 @@ struct LimitOffset {
size_t limit_;
};
-// Stack object which, on destruction, will update a vector of offsets based on
-// any supplied adjustments. To use, declare one of these, providing the
-// address of the offset vector to adjust. Then Add() any number of Adjustments
-// (each Adjustment gives the |original_offset| of a substring and the lengths
-// of the substring before and after transforming). When the OffsetAdjuster
-// goes out of scope, all the offsets in the provided vector will be updated.
-class BASE_EXPORT OffsetAdjuster {
- public:
- struct BASE_EXPORT Adjustment {
- Adjustment(size_t original_offset,
- size_t original_length,
- size_t output_length);
-
- size_t original_offset;
- size_t original_length;
- size_t output_length;
- };
-
- explicit OffsetAdjuster(std::vector<size_t>* offsets_for_adjustment);
- ~OffsetAdjuster();
-
- void Add(const Adjustment& adjustment);
-
- private:
- void AdjustOffset(std::vector<size_t>::iterator offset);
-
- std::vector<size_t>* offsets_for_adjustment_;
- std::vector<Adjustment> adjustments_;
-};
-
} // namespace base
#endif // BASE_STRINGS_UTF_OFFSET_STRING_CONVERSIONS_H_
diff --git a/chromium/base/strings/utf_offset_string_conversions_unittest.cc b/chromium/base/strings/utf_offset_string_conversions_unittest.cc
index 7626e4c0674..b50e1b69bfc 100644
--- a/chromium/base/strings/utf_offset_string_conversions_unittest.cc
+++ b/chromium/base/strings/utf_offset_string_conversions_unittest.cc
@@ -35,9 +35,11 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffset) {
{"A\xF0\x90\x8C\x80z", kNpos, kNpos},
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf8_to_utf16_cases); ++i) {
- size_t offset = utf8_to_utf16_cases[i].input_offset;
- UTF8ToUTF16AndAdjustOffset(utf8_to_utf16_cases[i].utf8, &offset);
- EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offset);
+ const size_t offset = utf8_to_utf16_cases[i].input_offset;
+ std::vector<size_t> offsets;
+ offsets.push_back(offset);
+ UTF8ToUTF16AndAdjustOffsets(utf8_to_utf16_cases[i].utf8, &offsets);
+ EXPECT_EQ(utf8_to_utf16_cases[i].output_offset, offsets[0]);
}
struct UTF16ToUTF8Case {
@@ -64,8 +66,10 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffset) {
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(utf16_to_utf8_cases); ++i) {
size_t offset = utf16_to_utf8_cases[i].input_offset;
- UTF16ToUTF8AndAdjustOffset(utf16_to_utf8_cases[i].utf16, &offset);
- EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offset);
+ std::vector<size_t> offsets;
+ offsets.push_back(offset);
+ UTF16ToUTF8AndAdjustOffsets(utf16_to_utf8_cases[i].utf16, &offsets);
+ EXPECT_EQ(utf16_to_utf8_cases[i].output_offset, offsets[0]) << i;
}
}
@@ -108,10 +112,9 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
std::vector<size_t> offsets;
for (size_t t = 0; t <= 9; ++t)
offsets.push_back(t);
- {
- OffsetAdjuster offset_adjuster(&offsets);
- offset_adjuster.Add(OffsetAdjuster::Adjustment(3, 3, 1));
- }
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
+ OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
size_t expected_1[] = {0, 1, 2, 3, kNpos, kNpos, 4, 5, 6, 7};
EXPECT_EQ(offsets.size(), arraysize(expected_1));
for (size_t i = 0; i < arraysize(expected_1); ++i)
@@ -123,13 +126,12 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
std::vector<size_t> offsets;
for (size_t t = 0; t <= 23; ++t)
offsets.push_back(t);
- {
- OffsetAdjuster offset_adjuster(&offsets);
- offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 1));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 2));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(10, 7, 4));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(20, 3, 1));
- }
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1));
+ adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2));
+ adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4));
+ adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1));
+ OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
size_t expected_2[] = {
0, kNpos, kNpos, 1, 2, kNpos, kNpos, kNpos, 4, 5, 6, kNpos, kNpos, kNpos,
kNpos, kNpos, kNpos, 10, 11, 12, 13, kNpos, kNpos, 14
@@ -144,13 +146,12 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
std::vector<size_t> offsets;
for (size_t t = 0; t <= 17; ++t)
offsets.push_back(t);
- {
- OffsetAdjuster offset_adjuster(&offsets);
- offset_adjuster.Add(OffsetAdjuster::Adjustment(0, 3, 0));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(4, 4, 4));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(11, 3, 3));
- offset_adjuster.Add(OffsetAdjuster::Adjustment(15, 2, 0));
- }
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0));
+ adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4));
+ adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3));
+ adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0));
+ OffsetAdjuster::AdjustOffsets(adjustments, &offsets);
size_t expected_3[] = {
0, kNpos, kNpos, 0, 1, kNpos, kNpos, kNpos, 5, 6, 7, 8, kNpos, kNpos, 11,
12, kNpos, 12
@@ -161,4 +162,135 @@ TEST(UTFOffsetStringConversionsTest, AdjustOffsets) {
}
}
+TEST(UTFOffsetStringConversionsTest, UnadjustOffsets) {
+ // Imagine we have strings as shown in the following cases where the
+ // X's represent encoded characters.
+ // 1: abcXXXdef ==> abcXdef
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t <= 7; ++t)
+ offsets.push_back(t);
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(3, 3, 1));
+ OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+ size_t expected_1[] = {0, 1, 2, 3, 6, 7, 8, 9};
+ EXPECT_EQ(offsets.size(), arraysize(expected_1));
+ for (size_t i = 0; i < arraysize(expected_1); ++i)
+ EXPECT_EQ(expected_1[i], offsets[i]);
+ }
+
+ // 2: XXXaXXXXbcXXXXXXXdefXXX ==> XaXXbcXXXXdefX
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t <= 14; ++t)
+ offsets.push_back(t);
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 1));
+ adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 2));
+ adjustments.push_back(OffsetAdjuster::Adjustment(10, 7, 4));
+ adjustments.push_back(OffsetAdjuster::Adjustment(20, 3, 1));
+ OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+ size_t expected_2[] = {
+ 0, 3, 4, kNpos, 8, 9, 10, kNpos, kNpos, kNpos, 17, 18, 19, 20, 23
+ };
+ EXPECT_EQ(offsets.size(), arraysize(expected_2));
+ for (size_t i = 0; i < arraysize(expected_2); ++i)
+ EXPECT_EQ(expected_2[i], offsets[i]);
+ }
+
+ // 3: XXXaXXXXbcdXXXeXX ==> aXXXXbcdXXXe
+ {
+ std::vector<size_t> offsets;
+ for (size_t t = 0; t <= 12; ++t)
+ offsets.push_back(t);
+ OffsetAdjuster::Adjustments adjustments;
+ adjustments.push_back(OffsetAdjuster::Adjustment(0, 3, 0));
+ adjustments.push_back(OffsetAdjuster::Adjustment(4, 4, 4));
+ adjustments.push_back(OffsetAdjuster::Adjustment(11, 3, 3));
+ adjustments.push_back(OffsetAdjuster::Adjustment(15, 2, 0));
+ OffsetAdjuster::UnadjustOffsets(adjustments, &offsets);
+ size_t expected_3[] = {
+ 0, // this could just as easily be 3
+ 4, kNpos, kNpos, kNpos, 8, 9, 10, 11, kNpos, kNpos, 14,
+ 15 // this could just as easily be 17
+ };
+ EXPECT_EQ(offsets.size(), arraysize(expected_3));
+ for (size_t i = 0; i < arraysize(expected_3); ++i)
+ EXPECT_EQ(expected_3[i], offsets[i]);
+ }
+}
+
+// MergeSequentialAdjustments is used by net/base/escape.{h,cc} and
+// net/base/net_util.{h,cc}. The two tests EscapeTest.AdjustOffset and
+// NetUtilTest.FormatUrlWithOffsets test its behavior extensively. This
+// is simply a short, additional test.
+TEST(UTFOffsetStringConversionsTest, MergeSequentialAdjustments) {
+ // Pretend the input string is "abcdefghijklmnopqrstuvwxyz".
+
+ // Set up |first_adjustments| to
+ // - remove the leading "a"
+ // - combine the "bc" into one character (call it ".")
+ // - remove the "f"
+ // - remove the "tuv"
+ // The resulting string should be ".deghijklmnopqrswxyz".
+ OffsetAdjuster::Adjustments first_adjustments;
+ first_adjustments.push_back(OffsetAdjuster::Adjustment(0, 1, 0));
+ first_adjustments.push_back(OffsetAdjuster::Adjustment(1, 2, 1));
+ first_adjustments.push_back(OffsetAdjuster::Adjustment(5, 1, 0));
+ first_adjustments.push_back(OffsetAdjuster::Adjustment(19, 3, 0));
+
+ // Set up |adjustments_on_adjusted_string| to
+ // - combine the "." character that replaced "bc" with "d" into one character
+ // (call it "?")
+ // - remove the "egh"
+ // - expand the "i" into two characters (call them "12")
+ // - combine the "jkl" into one character (call it "@")
+ // - expand the "z" into two characters (call it "34")
+ // The resulting string should be "?12@mnopqrswxy34".
+ OffsetAdjuster::Adjustments adjustments_on_adjusted_string;
+ adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+ 0, 2, 1));
+ adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+ 2, 3, 0));
+ adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+ 5, 1, 2));
+ adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+ 6, 3, 1));
+ adjustments_on_adjusted_string.push_back(OffsetAdjuster::Adjustment(
+ 19, 1, 2));
+
+ // Now merge the adjustments and check the results.
+ OffsetAdjuster::MergeSequentialAdjustments(first_adjustments,
+ &adjustments_on_adjusted_string);
+ // The merged adjustments should look like
+ // - combine abcd into "?"
+ // - note: it's also reasonable for the Merge function to instead produce
+ // two adjustments instead of this, one to remove a and another to
+ // combine bcd into "?". This test verifies the current behavior.
+ // - remove efgh
+ // - expand i into "12"
+ // - combine jkl into "@"
+ // - remove tuv
+ // - expand z into "34"
+ ASSERT_EQ(6u, adjustments_on_adjusted_string.size());
+ EXPECT_EQ(0u, adjustments_on_adjusted_string[0].original_offset);
+ EXPECT_EQ(4u, adjustments_on_adjusted_string[0].original_length);
+ EXPECT_EQ(1u, adjustments_on_adjusted_string[0].output_length);
+ EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_offset);
+ EXPECT_EQ(4u, adjustments_on_adjusted_string[1].original_length);
+ EXPECT_EQ(0u, adjustments_on_adjusted_string[1].output_length);
+ EXPECT_EQ(8u, adjustments_on_adjusted_string[2].original_offset);
+ EXPECT_EQ(1u, adjustments_on_adjusted_string[2].original_length);
+ EXPECT_EQ(2u, adjustments_on_adjusted_string[2].output_length);
+ EXPECT_EQ(9u, adjustments_on_adjusted_string[3].original_offset);
+ EXPECT_EQ(3u, adjustments_on_adjusted_string[3].original_length);
+ EXPECT_EQ(1u, adjustments_on_adjusted_string[3].output_length);
+ EXPECT_EQ(19u, adjustments_on_adjusted_string[4].original_offset);
+ EXPECT_EQ(3u, adjustments_on_adjusted_string[4].original_length);
+ EXPECT_EQ(0u, adjustments_on_adjusted_string[4].output_length);
+ EXPECT_EQ(25u, adjustments_on_adjusted_string[5].original_offset);
+ EXPECT_EQ(1u, adjustments_on_adjusted_string[5].original_length);
+ EXPECT_EQ(2u, adjustments_on_adjusted_string[5].output_length);
+}
+
} // namaspace base
diff --git a/chromium/base/strings/utf_string_conversions.cc b/chromium/base/strings/utf_string_conversions.cc
index c3ea4f253e5..f13ed1bb9c6 100644
--- a/chromium/base/strings/utf_string_conversions.cc
+++ b/chromium/base/strings/utf_string_conversions.cc
@@ -182,4 +182,9 @@ string16 ASCIIToUTF16(const StringPiece& ascii) {
return string16(ascii.begin(), ascii.end());
}
+std::string UTF16ToASCII(const string16& utf16) {
+ DCHECK(IsStringASCII(utf16)) << UTF16ToUTF8(utf16);
+ return std::string(utf16.begin(), utf16.end());
+}
+
} // namespace base
diff --git a/chromium/base/strings/utf_string_conversions.h b/chromium/base/strings/utf_string_conversions.h
index 3461aa491df..13e0b7193be 100644
--- a/chromium/base/strings/utf_string_conversions.h
+++ b/chromium/base/strings/utf_string_conversions.h
@@ -39,34 +39,15 @@ BASE_EXPORT bool UTF16ToUTF8(const char16* src, size_t src_len,
std::string* output);
BASE_EXPORT std::string UTF16ToUTF8(const string16& utf16);
-// We are trying to get rid of wstring as much as possible, but it's too big
-// a mess to do it all at once. These conversions should be used when we
-// really should just be passing a string16 around, but we haven't finished
-// porting whatever module uses wstring and the conversion is being used as a
-// stopcock. This makes it easy to grep for the ones that should be removed.
-#if defined(OS_WIN)
-# define WideToUTF16Hack
-# define UTF16ToWideHack
-#else
-# define WideToUTF16Hack WideToUTF16
-# define UTF16ToWideHack UTF16ToWide
-#endif
-
// These convert an ASCII string, typically a hardcoded constant, to a
// UTF16/Wide string.
BASE_EXPORT std::wstring ASCIIToWide(const StringPiece& ascii);
BASE_EXPORT string16 ASCIIToUTF16(const StringPiece& ascii);
-} // namespace base
+// Converts to 7-bit ASCII by truncating. The result must be known to be ASCII
+// beforehand.
+BASE_EXPORT std::string UTF16ToASCII(const string16& utf16);
-// TODO(brettw) remove these when callers are fixed up.
-using base::WideToUTF8;
-using base::UTF8ToWide;
-using base::WideToUTF16;
-using base::UTF16ToWide;
-using base::UTF8ToUTF16;
-using base::UTF16ToUTF8;
-using base::ASCIIToWide;
-using base::ASCIIToUTF16;
+} // namespace base
#endif // BASE_STRINGS_UTF_STRING_CONVERSIONS_H_
diff --git a/chromium/base/supports_user_data.cc b/chromium/base/supports_user_data.cc
index 2a0263ed0d1..9689014d4ad 100644
--- a/chromium/base/supports_user_data.cc
+++ b/chromium/base/supports_user_data.cc
@@ -35,6 +35,11 @@ void SupportsUserData::DetachUserDataThread() {
SupportsUserData::~SupportsUserData() {
DCHECK(thread_checker_.CalledOnValidThread() || user_data_.empty());
+ DataMap local_user_data;
+ user_data_.swap(local_user_data);
+ // Now this->user_data_ is empty, and any destructors called transitively from
+ // the destruction of |local_user_data| will see it that way instead of
+ // examining a being-destroyed object.
}
} // namespace base
diff --git a/chromium/base/supports_user_data_unittest.cc b/chromium/base/supports_user_data_unittest.cc
new file mode 100644
index 00000000000..f6afc37dc28
--- /dev/null
+++ b/chromium/base/supports_user_data_unittest.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/supports_user_data.h"
+
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+struct TestSupportsUserData : public SupportsUserData {};
+
+struct UsesItself : public SupportsUserData::Data {
+ UsesItself(SupportsUserData* supports_user_data, const void* key)
+ : supports_user_data_(supports_user_data),
+ key_(key) {
+ }
+
+ virtual ~UsesItself() {
+ EXPECT_EQ(NULL, supports_user_data_->GetUserData(key_));
+ }
+
+ SupportsUserData* supports_user_data_;
+ const void* key_;
+};
+
+TEST(SupportsUserDataTest, ClearWorksRecursively) {
+ TestSupportsUserData supports_user_data;
+ char key = 0;
+ supports_user_data.SetUserData(&key,
+ new UsesItself(&supports_user_data, &key));
+ // Destruction of supports_user_data runs the actual test.
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/sync_socket_posix.cc b/chromium/base/sync_socket_posix.cc
index 5bb893aae87..6d397ffa2ec 100644
--- a/chromium/base/sync_socket_posix.cc
+++ b/chromium/base/sync_socket_posix.cc
@@ -36,7 +36,7 @@ size_t SendHelper(SyncSocket::Handle handle,
DCHECK_LE(length, kMaxMessageLength);
DCHECK_NE(handle, SyncSocket::kInvalidHandle);
const char* charbuffer = static_cast<const char*>(buffer);
- const int len = file_util::WriteFileDescriptor(handle, charbuffer, length);
+ const int len = WriteFileDescriptor(handle, charbuffer, length);
return len < 0 ? 0 : static_cast<size_t>(len);
}
diff --git a/chromium/base/synchronization/condition_variable_posix.cc b/chromium/base/synchronization/condition_variable_posix.cc
index e70a301cb2a..8492a01fbc5 100644
--- a/chromium/base/synchronization/condition_variable_posix.cc
+++ b/chromium/base/synchronization/condition_variable_posix.cc
@@ -23,10 +23,12 @@ ConditionVariable::ConditionVariable(Lock* user_lock)
int rv = 0;
// http://crbug.com/293736
// NaCl doesn't support monotonic clock based absolute deadlines.
- // Android supports it through the non-standard
- // pthread_cond_timedwait_monotonic_np.
+ // On older Android platform versions, it's supported through the
+ // non-standard pthread_cond_timedwait_monotonic_np. Newer platform
+ // versions have pthread_condattr_setclock.
// Mac can use relative time deadlines.
-#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID)
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && \
+ !(defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC))
pthread_condattr_t attrs;
rv = pthread_condattr_init(&attrs);
DCHECK_EQ(0, rv);
@@ -93,12 +95,12 @@ void ConditionVariable::TimedWait(const TimeDelta& max_time) {
absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && defined(HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC)
int rv = pthread_cond_timedwait_monotonic_np(
&condition_, user_mutex_, &absolute_time);
#else
int rv = pthread_cond_timedwait(&condition_, user_mutex_, &absolute_time);
-#endif // OS_ANDROID
+#endif // OS_ANDROID && HAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC
#endif // OS_MACOSX
DCHECK(rv == 0 || rv == ETIMEDOUT);
diff --git a/chromium/base/synchronization/condition_variable_unittest.cc b/chromium/base/synchronization/condition_variable_unittest.cc
index ee554ff329f..5f947a9567f 100644
--- a/chromium/base/synchronization/condition_variable_unittest.cc
+++ b/chromium/base/synchronization/condition_variable_unittest.cc
@@ -101,7 +101,6 @@ class WorkQueue : public PlatformThread::Delegate {
int GetNumThreadsTakingAssignments() const;
int GetNumThreadsCompletingTasks() const;
int GetNumberOfCompletedTasks() const;
- TimeDelta GetWorkTime() const;
void SetWorkTime(TimeDelta delay);
void SetTaskCount(int count);
@@ -651,10 +650,6 @@ int WorkQueue::GetNumberOfCompletedTasks() const {
return total;
}
-TimeDelta WorkQueue::GetWorkTime() const {
- return worker_delay_;
-}
-
void WorkQueue::SetWorkTime(TimeDelta delay) {
worker_delay_ = delay;
}
diff --git a/chromium/base/synchronization/lock.cc b/chromium/base/synchronization/lock.cc
index 49efbe92651..7c7ee9dc5a9 100644
--- a/chromium/base/synchronization/lock.cc
+++ b/chromium/base/synchronization/lock.cc
@@ -13,34 +13,25 @@
namespace base {
-const PlatformThreadId kNoThreadId = static_cast<PlatformThreadId>(0);
-
Lock::Lock() : lock_() {
- owned_by_thread_ = false;
- owning_thread_id_ = kNoThreadId;
}
Lock::~Lock() {
- DCHECK(!owned_by_thread_);
- DCHECK_EQ(kNoThreadId, owning_thread_id_);
+ DCHECK(owning_thread_ref_.is_null());
}
void Lock::AssertAcquired() const {
- DCHECK(owned_by_thread_);
- DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
+ DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
}
void Lock::CheckHeldAndUnmark() {
- DCHECK(owned_by_thread_);
- DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
- owned_by_thread_ = false;
- owning_thread_id_ = kNoThreadId;
+ DCHECK(owning_thread_ref_ == PlatformThread::CurrentRef());
+ owning_thread_ref_ = PlatformThreadRef();
}
void Lock::CheckUnheldAndMark() {
- DCHECK(!owned_by_thread_);
- owned_by_thread_ = true;
- owning_thread_id_ = PlatformThread::CurrentId();
+ DCHECK(owning_thread_ref_.is_null());
+ owning_thread_ref_ = PlatformThread::CurrentRef();
}
} // namespace base
diff --git a/chromium/base/synchronization/lock.h b/chromium/base/synchronization/lock.h
index 7e8ffe7b741..9c0d062a900 100644
--- a/chromium/base/synchronization/lock.h
+++ b/chromium/base/synchronization/lock.h
@@ -80,11 +80,7 @@ class BASE_EXPORT Lock {
// All private data is implicitly protected by lock_.
// Be VERY careful to only access members under that lock.
-
- // Determines validity of owning_thread_id_. Needed as we don't have
- // a null owning_thread_id_ value.
- bool owned_by_thread_;
- base::PlatformThreadId owning_thread_id_;
+ base::PlatformThreadRef owning_thread_ref_;
#endif // NDEBUG
// Platform specific underlying lock implementation.
diff --git a/chromium/base/synchronization/waitable_event_win.cc b/chromium/base/synchronization/waitable_event_win.cc
index 8259cb74cfc..04746b597b4 100644
--- a/chromium/base/synchronization/waitable_event_win.cc
+++ b/chromium/base/synchronization/waitable_event_win.cc
@@ -92,7 +92,7 @@ size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
FALSE, // don't wait for all the objects
INFINITE); // no timeout
if (result >= WAIT_OBJECT_0 + count) {
- DLOG_GETLASTERROR(FATAL) << "WaitForMultipleObjects failed";
+ DPLOG(FATAL) << "WaitForMultipleObjects failed";
return 0;
}
diff --git a/chromium/base/sys_byteorder.h b/chromium/base/sys_byteorder.h
index 17b662c58f0..704ed568b09 100644
--- a/chromium/base/sys_byteorder.h
+++ b/chromium/base/sys_byteorder.h
@@ -20,59 +20,27 @@
#include <arpa/inet.h>
#endif
-// Include headers to provide byteswap for all platforms.
-#if defined(COMPILER_MSVC)
-#include <stdlib.h>
-#elif defined(OS_MACOSX)
-#include <libkern/OSByteOrder.h>
-#elif defined(OS_BSD)
-#include <sys/endian.h>
-#else
-#include <byteswap.h>
-#endif
-
-
namespace base {
// Returns a value with all bytes in |x| swapped, i.e. reverses the endianness.
inline uint16 ByteSwap(uint16 x) {
-#if defined(COMPILER_MSVC)
- return _byteswap_ushort(x);
-#elif defined(OS_MACOSX)
- return OSSwapInt16(x);
-#elif defined(OS_OPENBSD)
- return swap16(x);
-#elif defined(OS_FREEBSD)
- return bswap16(x);
-#else
- return bswap_16(x);
-#endif
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
}
+
inline uint32 ByteSwap(uint32 x) {
-#if defined(COMPILER_MSVC)
- return _byteswap_ulong(x);
-#elif defined(OS_MACOSX)
- return OSSwapInt32(x);
-#elif defined(OS_OPENBSD)
- return swap32(x);
-#elif defined(OS_FREEBSD)
- return bswap32(x);
-#else
- return bswap_32(x);
-#endif
+ return ((x & 0x000000fful) << 24) | ((x & 0x0000ff00ul) << 8) |
+ ((x & 0x00ff0000ul) >> 8) | ((x & 0xff000000ul) >> 24);
}
+
inline uint64 ByteSwap(uint64 x) {
-#if defined(COMPILER_MSVC)
- return _byteswap_uint64(x);
-#elif defined(OS_MACOSX)
- return OSSwapInt64(x);
-#elif defined(OS_OPENBSD)
- return swap64(x);
-#elif defined(OS_FREEBSD)
- return bswap64(x);
-#else
- return bswap_64(x);
-#endif
+ return ((x & 0x00000000000000ffull) << 56) |
+ ((x & 0x000000000000ff00ull) << 40) |
+ ((x & 0x0000000000ff0000ull) << 24) |
+ ((x & 0x00000000ff000000ull) << 8) |
+ ((x & 0x000000ff00000000ull) >> 8) |
+ ((x & 0x0000ff0000000000ull) >> 24) |
+ ((x & 0x00ff000000000000ull) >> 40) |
+ ((x & 0xff00000000000000ull) >> 56);
}
// Converts the bytes in |x| from host order (endianness) to little endian, and
@@ -149,5 +117,4 @@ inline uint64 HostToNet64(uint64 x) {
} // namespace base
-
#endif // BASE_SYS_BYTEORDER_H_
diff --git a/chromium/base/sys_info.h b/chromium/base/sys_info.h
index aa40cadfbc9..7ce4e659f28 100644
--- a/chromium/base/sys_info.h
+++ b/chromium/base/sys_info.h
@@ -28,11 +28,22 @@ class BASE_EXPORT SysInfo {
// machine.
static int64 AmountOfAvailablePhysicalMemory();
+ // Return the number of bytes of virtual memory of this process. A return
+ // value of zero means that there is no limit on the available virtual
+ // memory.
+ static int64 AmountOfVirtualMemory();
+
// Return the number of megabytes of physical memory on the current machine.
static int AmountOfPhysicalMemoryMB() {
return static_cast<int>(AmountOfPhysicalMemory() / 1024 / 1024);
}
+ // Return the number of megabytes of available virtual memory, or zero if it
+ // is unlimited.
+ static int AmountOfVirtualMemoryMB() {
+ return static_cast<int>(AmountOfVirtualMemory() / 1024 / 1024);
+ }
+
// Return the available disk space in bytes on the volume containing |path|,
// or -1 on failure.
static int64 AmountOfFreeDiskSpace(const FilePath& path);
diff --git a/chromium/base/sys_info_android.cc b/chromium/base/sys_info_android.cc
index 577ca0e6bd1..8884e15d464 100644
--- a/chromium/base/sys_info_android.cc
+++ b/chromium/base/sys_info_android.cc
@@ -4,22 +4,64 @@
#include "base/sys_info.h"
+#include <dlfcn.h>
#include <sys/system_properties.h>
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/stringprintf.h"
+#include "base/sys_info_internal.h"
+
+// TODO(rmcilroy): Update API level when 'L' gets an official API level.
+#if (__ANDROID_API__ >= 9999 /* 'L' */)
+
+namespace {
+
+typedef int (SystemPropertyGetFunction)(const char*, char*);
+
+SystemPropertyGetFunction* DynamicallyLoadRealSystemPropertyGet() {
+ // libc.so should already be open, get a handle to it.
+ void* handle = dlopen("libc.so", RTLD_NOLOAD);
+ if (!handle) {
+ LOG(FATAL) << "Cannot dlopen libc.so: " << dlerror();
+ }
+ SystemPropertyGetFunction* real_system_property_get =
+ reinterpret_cast<SystemPropertyGetFunction*>(
+ dlsym(handle, "__system_property_get"));
+ if (!real_system_property_get) {
+ LOG(FATAL) << "Cannot resolve __system_property_get(): " << dlerror();
+ }
+ return real_system_property_get;
+}
+
+static base::LazyInstance<base::internal::LazySysInfoValue<
+ SystemPropertyGetFunction*, DynamicallyLoadRealSystemPropertyGet> >::Leaky
+ g_lazy_real_system_property_get = LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+// Android 'L' removes __system_property_get from the NDK, however it is still
+// a hidden symbol in libc. Until we remove all calls of __system_property_get
+// from Chrome we work around this by defining a weak stub here, which uses
+// dlsym to but ensures that Chrome uses the real system
+// implementatation when loaded. http://crbug.com/392191.
+int __system_property_get(const char* name, char* value) {
+ return g_lazy_real_system_property_get.Get().value()(name, value);
+}
+
+#endif
namespace {
// Default version of Android to fall back to when actual version numbers
-// cannot be acquired.
-// TODO(dfalcantara): Keep this reasonably up to date with the latest publicly
-// available version of Android.
+// cannot be acquired. Use the latest Android release with a higher bug fix
+// version to avoid unnecessarily comparison errors with the latest release.
+// This should be manually kept up-to-date on each Android release.
const int kDefaultAndroidMajorVersion = 4;
-const int kDefaultAndroidMinorVersion = 3;
-const int kDefaultAndroidBugfixVersion = 0;
+const int kDefaultAndroidMinorVersion = 4;
+const int kDefaultAndroidBugfixVersion = 99;
// Parse out the OS version numbers from the system properties.
void ParseOSVersionNumbers(const char* os_version_str,
diff --git a/chromium/base/sys_info_chromeos.cc b/chromium/base/sys_info_chromeos.cc
index 7cf69753460..4f32f9bfe63 100644
--- a/chromium/base/sys_info_chromeos.cc
+++ b/chromium/base/sys_info_chromeos.cc
@@ -7,6 +7,7 @@
#include "base/basictypes.h"
#include "base/environment.h"
#include "base/file_util.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/strings/string_number_conversions.h"
@@ -71,7 +72,7 @@ class ChromeOSVersionInfo {
ThreadRestrictions::ScopedAllowIO allow_io;
FilePath path(kLinuxStandardBaseReleaseFile);
ReadFileToString(path, &lsb_release);
- PlatformFileInfo fileinfo;
+ File::Info fileinfo;
if (GetFileInfo(path, &fileinfo))
lsb_release_time_ = fileinfo.creation_time;
}
diff --git a/chromium/base/sys_info_ios.mm b/chromium/base/sys_info_ios.mm
index de68fcf37c1..49d618c416f 100644
--- a/chromium/base/sys_info_ios.mm
+++ b/chromium/base/sys_info_ios.mm
@@ -65,7 +65,7 @@ void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
int64 SysInfo::AmountOfPhysicalMemory() {
struct host_basic_info hostinfo;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
- base::mac::ScopedMachPort host(mach_host_self());
+ base::mac::ScopedMachSendRight host(mach_host_self());
int result = host_info(host,
HOST_BASIC_INFO,
reinterpret_cast<host_info_t>(&hostinfo),
@@ -79,6 +79,23 @@ int64 SysInfo::AmountOfPhysicalMemory() {
}
// static
+int64 SysInfo::AmountOfAvailablePhysicalMemory() {
+ base::mac::ScopedMachSendRight host(mach_host_self());
+ vm_statistics_data_t vm_info;
+ mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
+ if (host_statistics(host.get(),
+ HOST_VM_INFO,
+ reinterpret_cast<host_info_t>(&vm_info),
+ &count) != KERN_SUCCESS) {
+ NOTREACHED();
+ return 0;
+ }
+
+ return static_cast<int64>(
+ vm_info.free_count - vm_info.speculative_count) * PAGE_SIZE;
+}
+
+// static
std::string SysInfo::CPUModelName() {
char name[256];
size_t len = arraysize(name);
diff --git a/chromium/base/sys_info_mac.cc b/chromium/base/sys_info_mac.cc
index 2911c7c4ce3..57e1f835e32 100644
--- a/chromium/base/sys_info_mac.cc
+++ b/chromium/base/sys_info_mac.cc
@@ -45,7 +45,7 @@ void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
int64 SysInfo::AmountOfPhysicalMemory() {
struct host_basic_info hostinfo;
mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
- base::mac::ScopedMachPort host(mach_host_self());
+ base::mac::ScopedMachSendRight host(mach_host_self());
int result = host_info(host,
HOST_BASIC_INFO,
reinterpret_cast<host_info_t>(&hostinfo),
@@ -60,7 +60,7 @@ int64 SysInfo::AmountOfPhysicalMemory() {
// static
int64 SysInfo::AmountOfAvailablePhysicalMemory() {
- base::mac::ScopedMachPort host(mach_host_self());
+ base::mac::ScopedMachSendRight host(mach_host_self());
vm_statistics_data_t vm_info;
mach_msg_type_number_t count = HOST_VM_INFO_COUNT;
diff --git a/chromium/base/sys_info_posix.cc b/chromium/base/sys_info_posix.cc
index 07d08b72bbc..90baa69a2f6 100644
--- a/chromium/base/sys_info_posix.cc
+++ b/chromium/base/sys_info_posix.cc
@@ -7,6 +7,7 @@
#include <errno.h>
#include <string.h>
#include <sys/param.h>
+#include <sys/resource.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -45,6 +46,20 @@ base::LazyInstance<
g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER;
#endif
+int64 AmountOfVirtualMemory() {
+ struct rlimit limit;
+ int result = getrlimit(RLIMIT_DATA, &limit);
+ if (result != 0) {
+ NOTREACHED();
+ return 0;
+ }
+ return limit.rlim_cur == RLIM_INFINITY ? 0 : limit.rlim_cur;
+}
+
+base::LazyInstance<
+ base::internal::LazySysInfoValue<int64, AmountOfVirtualMemory> >::Leaky
+ g_lazy_virtual_memory = LAZY_INSTANCE_INITIALIZER;
+
} // namespace
namespace base {
@@ -56,6 +71,11 @@ int SysInfo::NumberOfProcessors() {
#endif
// static
+int64 SysInfo::AmountOfVirtualMemory() {
+ return g_lazy_virtual_memory.Get().value();
+}
+
+// static
int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
base::ThreadRestrictions::AssertIOAllowed();
diff --git a/chromium/base/sys_info_unittest.cc b/chromium/base/sys_info_unittest.cc
index 93eff777c9b..e771b2b9879 100644
--- a/chromium/base/sys_info_unittest.cc
+++ b/chromium/base/sys_info_unittest.cc
@@ -29,6 +29,8 @@ TEST_F(SysInfoTest, AmountOfMem) {
// We aren't actually testing that it's correct, just that it's sane.
EXPECT_GT(base::SysInfo::AmountOfPhysicalMemory(), 0);
EXPECT_GT(base::SysInfo::AmountOfPhysicalMemoryMB(), 0);
+ // The maxmimal amount of virtual memory can be zero which means unlimited.
+ EXPECT_GE(base::SysInfo::AmountOfVirtualMemory(), 0);
}
TEST_F(SysInfoTest, AmountOfFreeDiskSpace) {
diff --git a/chromium/base/sys_info_win.cc b/chromium/base/sys_info_win.cc
index 191fd94ff5e..9cc0cfa48a3 100644
--- a/chromium/base/sys_info_win.cc
+++ b/chromium/base/sys_info_win.cc
@@ -49,6 +49,11 @@ int64 SysInfo::AmountOfAvailablePhysicalMemory() {
}
// static
+int64 SysInfo::AmountOfVirtualMemory() {
+ return 0;
+}
+
+// static
int64 SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
base::ThreadRestrictions::AssertIOAllowed();
diff --git a/chromium/base/task/cancelable_task_tracker.cc b/chromium/base/task/cancelable_task_tracker.cc
new file mode 100644
index 00000000000..801223e2806
--- /dev/null
+++ b/chromium/base/task/cancelable_task_tracker.cc
@@ -0,0 +1,187 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/cancelable_task_tracker.h"
+
+#include <utility>
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "base/synchronization/cancellation_flag.h"
+#include "base/task_runner.h"
+
+using base::Bind;
+using base::CancellationFlag;
+using base::Closure;
+using base::hash_map;
+using base::TaskRunner;
+
+namespace {
+
+void RunIfNotCanceled(const CancellationFlag* flag, const Closure& task) {
+ if (!flag->IsSet())
+ task.Run();
+}
+
+void RunIfNotCanceledThenUntrack(const CancellationFlag* flag,
+ const Closure& task,
+ const Closure& untrack) {
+ RunIfNotCanceled(flag, task);
+ untrack.Run();
+}
+
+bool IsCanceled(const CancellationFlag* flag,
+ base::ScopedClosureRunner* cleanup_runner) {
+ return flag->IsSet();
+}
+
+void RunAndDeleteFlag(const Closure& closure, const CancellationFlag* flag) {
+ closure.Run();
+ delete flag;
+}
+
+void RunOrPostToTaskRunner(TaskRunner* task_runner, const Closure& closure) {
+ if (task_runner->RunsTasksOnCurrentThread())
+ closure.Run();
+ else
+ task_runner->PostTask(FROM_HERE, closure);
+}
+
+} // namespace
+
+namespace base {
+
+// static
+const CancelableTaskTracker::TaskId CancelableTaskTracker::kBadTaskId = 0;
+
+CancelableTaskTracker::CancelableTaskTracker()
+ : weak_factory_(this), next_id_(1) {}
+
+CancelableTaskTracker::~CancelableTaskTracker() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ TryCancelAll();
+}
+
+CancelableTaskTracker::TaskId CancelableTaskTracker::PostTask(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const Closure& task) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ return PostTaskAndReply(task_runner, from_here, task, Bind(&base::DoNothing));
+}
+
+CancelableTaskTracker::TaskId CancelableTaskTracker::PostTaskAndReply(
+ TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const Closure& task,
+ const Closure& reply) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ // We need a MessageLoop to run reply.
+ DCHECK(base::MessageLoopProxy::current().get());
+
+ // Owned by reply callback below.
+ CancellationFlag* flag = new CancellationFlag();
+
+ TaskId id = next_id_;
+ next_id_++; // int64 is big enough that we ignore the potential overflow.
+
+ const Closure& untrack_closure =
+ Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id);
+ bool success =
+ task_runner->PostTaskAndReply(from_here,
+ Bind(&RunIfNotCanceled, flag, task),
+ Bind(&RunIfNotCanceledThenUntrack,
+ base::Owned(flag),
+ reply,
+ untrack_closure));
+
+ if (!success)
+ return kBadTaskId;
+
+ Track(id, flag);
+ return id;
+}
+
+CancelableTaskTracker::TaskId CancelableTaskTracker::NewTrackedTaskId(
+ IsCanceledCallback* is_canceled_cb) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(base::MessageLoopProxy::current().get());
+
+ TaskId id = next_id_;
+ next_id_++; // int64 is big enough that we ignore the potential overflow.
+
+ // Will be deleted by |untrack_and_delete_flag| after Untrack().
+ CancellationFlag* flag = new CancellationFlag();
+
+ Closure untrack_and_delete_flag = Bind(
+ &RunAndDeleteFlag,
+ Bind(&CancelableTaskTracker::Untrack, weak_factory_.GetWeakPtr(), id),
+ flag);
+
+ // Will always run |untrack_and_delete_flag| on current MessageLoop.
+ base::ScopedClosureRunner* untrack_and_delete_flag_runner =
+ new base::ScopedClosureRunner(Bind(&RunOrPostToTaskRunner,
+ base::MessageLoopProxy::current(),
+ untrack_and_delete_flag));
+
+ *is_canceled_cb =
+ Bind(&IsCanceled, flag, base::Owned(untrack_and_delete_flag_runner));
+
+ Track(id, flag);
+ return id;
+}
+
+void CancelableTaskTracker::TryCancel(TaskId id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ hash_map<TaskId, CancellationFlag*>::const_iterator it = task_flags_.find(id);
+ if (it == task_flags_.end()) {
+ // Two possibilities:
+ //
+ // 1. The task has already been untracked.
+ // 2. The TaskId is bad or unknown.
+ //
+ // Since this function is best-effort, it's OK to ignore these.
+ return;
+ }
+ it->second->Set();
+}
+
+void CancelableTaskTracker::TryCancelAll() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ for (hash_map<TaskId, CancellationFlag*>::const_iterator it =
+ task_flags_.begin();
+ it != task_flags_.end();
+ ++it) {
+ it->second->Set();
+ }
+}
+
+bool CancelableTaskTracker::HasTrackedTasks() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return !task_flags_.empty();
+}
+
+void CancelableTaskTracker::Track(TaskId id, CancellationFlag* flag) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+
+ bool success = task_flags_.insert(std::make_pair(id, flag)).second;
+ DCHECK(success);
+}
+
+void CancelableTaskTracker::Untrack(TaskId id) {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ size_t num = task_flags_.erase(id);
+ DCHECK_EQ(1u, num);
+}
+
+} // namespace base
diff --git a/chromium/base/task/cancelable_task_tracker.h b/chromium/base/task/cancelable_task_tracker.h
new file mode 100644
index 00000000000..21cb45df1c0
--- /dev/null
+++ b/chromium/base/task/cancelable_task_tracker.h
@@ -0,0 +1,120 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// CancelableTaskTracker posts tasks (in the form of a Closure) to a
+// TaskRunner, and is able to cancel the task later if it's not needed
+// anymore. On destruction, CancelableTaskTracker will cancel all
+// tracked tasks.
+//
+// Each cancelable task can be associated with a reply (also a Closure). After
+// the task is run on the TaskRunner, |reply| will be posted back to
+// originating TaskRunner.
+//
+// NOTE:
+//
+// CancelableCallback (base/cancelable_callback.h) and WeakPtr binding are
+// preferred solutions for canceling a task. However, they don't support
+// cancelation from another thread. This is sometimes a performance critical
+// requirement. E.g. We need to cancel database lookup task on DB thread when
+// user changes inputed text. If it is performance critical to do a best effort
+// cancelation of a task, then CancelableTaskTracker is appropriate,
+// otherwise use one of the other mechanisms.
+//
+// THREAD-SAFETY:
+//
+// 1. CancelableTaskTracker objects are not thread safe. They must
+// be created, used, and destroyed on the originating thread that posts the
+// task. It's safe to destroy a CancelableTaskTracker while there
+// are outstanding tasks. This is commonly used to cancel all outstanding
+// tasks.
+//
+// 2. Both task and reply are deleted on the originating thread.
+//
+// 3. IsCanceledCallback is thread safe and can be run or deleted on any
+// thread.
+#ifndef BASE_TASK_CANCELABLE_TASK_TRACKER_H_
+#define BASE_TASK_CANCELABLE_TASK_TRACKER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/weak_ptr.h"
+#include "base/threading/thread_checker.h"
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace base {
+
+class CancellationFlag;
+class TaskRunner;
+
+class BASE_EXPORT CancelableTaskTracker {
+ public:
+ // All values except kBadTaskId are valid.
+ typedef int64 TaskId;
+ static const TaskId kBadTaskId;
+
+ typedef base::Callback<bool()> IsCanceledCallback;
+
+ CancelableTaskTracker();
+
+ // Cancels all tracked tasks.
+ ~CancelableTaskTracker();
+
+ TaskId PostTask(base::TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task);
+
+ TaskId PostTaskAndReply(base::TaskRunner* task_runner,
+ const tracked_objects::Location& from_here,
+ const base::Closure& task,
+ const base::Closure& reply);
+
+ // Creates a tracked TaskId and an associated IsCanceledCallback. Client can
+ // later call TryCancel() with the returned TaskId, and run |is_canceled_cb|
+ // from any thread to check whether the TaskId is canceled.
+ //
+ // The returned task ID is tracked until the last copy of
+ // |is_canceled_cb| is destroyed.
+ //
+ // Note. This function is used to address some special cancelation requirement
+ // in existing code. You SHOULD NOT need this function in new code.
+ TaskId NewTrackedTaskId(IsCanceledCallback* is_canceled_cb);
+
+ // After calling this function, |task| and |reply| will not run. If the
+ // cancelation happens when |task| is running or has finished running, |reply|
+ // will not run. If |reply| is running or has finished running, cancellation
+ // is a noop.
+ //
+ // Note. It's OK to cancel a |task| for more than once. The later calls are
+ // noops.
+ void TryCancel(TaskId id);
+
+ // It's OK to call this function for more than once. The later calls are
+ // noops.
+ void TryCancelAll();
+
+ // Returns true iff there are in-flight tasks that are still being
+ // tracked.
+ bool HasTrackedTasks() const;
+
+ private:
+ void Track(TaskId id, base::CancellationFlag* flag);
+ void Untrack(TaskId id);
+
+ base::hash_map<TaskId, base::CancellationFlag*> task_flags_;
+ base::WeakPtrFactory<CancelableTaskTracker> weak_factory_;
+
+ TaskId next_id_;
+ base::ThreadChecker thread_checker_;
+
+ DISALLOW_COPY_AND_ASSIGN(CancelableTaskTracker);
+};
+
+} // namespace base
+
+#endif // BASE_TASK_CANCELABLE_TASK_TRACKER_H_
diff --git a/chromium/base/task/cancelable_task_tracker_unittest.cc b/chromium/base/task/cancelable_task_tracker_unittest.cc
new file mode 100644
index 00000000000..e122e8deef0
--- /dev/null
+++ b/chromium/base/task/cancelable_task_tracker_unittest.cc
@@ -0,0 +1,435 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/task/cancelable_task_tracker.h"
+
+#include <cstddef>
+#include <deque>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/test/test_simple_task_runner.h"
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+namespace {
+
+class CancelableTaskTrackerTest : public testing::Test {
+ protected:
+ virtual ~CancelableTaskTrackerTest() { RunCurrentLoopUntilIdle(); }
+
+ void RunCurrentLoopUntilIdle() {
+ RunLoop run_loop;
+ run_loop.RunUntilIdle();
+ }
+
+ CancelableTaskTracker task_tracker_;
+
+ private:
+ // Needed by CancelableTaskTracker methods.
+ MessageLoop message_loop_;
+};
+
+void AddFailureAt(const tracked_objects::Location& location) {
+ ADD_FAILURE_AT(location.file_name(), location.line_number());
+}
+
+// Returns a closure that fails if run.
+Closure MakeExpectedNotRunClosure(const tracked_objects::Location& location) {
+ return Bind(&AddFailureAt, location);
+}
+
+// A helper class for MakeExpectedRunClosure() that fails if it is
+// destroyed without Run() having been called. This class may be used
+// from multiple threads as long as Run() is called at most once
+// before destruction.
+class RunChecker {
+ public:
+ explicit RunChecker(const tracked_objects::Location& location)
+ : location_(location), called_(false) {}
+
+ ~RunChecker() {
+ if (!called_) {
+ ADD_FAILURE_AT(location_.file_name(), location_.line_number());
+ }
+ }
+
+ void Run() { called_ = true; }
+
+ private:
+ tracked_objects::Location location_;
+ bool called_;
+};
+
+// Returns a closure that fails on destruction if it hasn't been run.
+Closure MakeExpectedRunClosure(const tracked_objects::Location& location) {
+ return Bind(&RunChecker::Run, Owned(new RunChecker(location)));
+}
+
+} // namespace
+
+// With the task tracker, post a task, a task with a reply, and get a
+// new task id without canceling any of them. The tasks and the reply
+// should run and the "is canceled" callback should return false.
+TEST_F(CancelableTaskTrackerTest, NoCancel) {
+ Thread worker_thread("worker thread");
+ ASSERT_TRUE(worker_thread.Start());
+
+ ignore_result(task_tracker_.PostTask(worker_thread.message_loop_proxy().get(),
+ FROM_HERE,
+ MakeExpectedRunClosure(FROM_HERE)));
+
+ ignore_result(
+ task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
+ FROM_HERE,
+ MakeExpectedRunClosure(FROM_HERE),
+ MakeExpectedRunClosure(FROM_HERE)));
+
+ CancelableTaskTracker::IsCanceledCallback is_canceled;
+ ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
+
+ worker_thread.Stop();
+
+ RunCurrentLoopUntilIdle();
+
+ EXPECT_FALSE(is_canceled.Run());
+}
+
+// Post a task with the task tracker but cancel it before running the
+// task runner. The task should not run.
+TEST_F(CancelableTaskTrackerTest, CancelPostedTask) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
+ test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size());
+
+ task_tracker_.TryCancel(task_id);
+
+ test_task_runner->RunUntilIdle();
+}
+
+// Post a task with reply with the task tracker and cancel it before
+// running the task runner. Neither the task nor the reply should
+// run.
+TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ CancelableTaskTracker::TaskId task_id =
+ task_tracker_.PostTaskAndReply(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedNotRunClosure(FROM_HERE),
+ MakeExpectedNotRunClosure(FROM_HERE));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ task_tracker_.TryCancel(task_id);
+
+ test_task_runner->RunUntilIdle();
+}
+
+// Post a task with reply with the task tracker and cancel it after
+// running the task runner but before running the current message
+// loop. The task should run but the reply should not.
+TEST_F(CancelableTaskTrackerTest, CancelReply) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ CancelableTaskTracker::TaskId task_id =
+ task_tracker_.PostTaskAndReply(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedRunClosure(FROM_HERE),
+ MakeExpectedNotRunClosure(FROM_HERE));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ test_task_runner->RunUntilIdle();
+
+ task_tracker_.TryCancel(task_id);
+}
+
+// Post a task with reply with the task tracker on a worker thread and
+// cancel it before running the current message loop. The task should
+// run but the reply should not.
+TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) {
+ Thread worker_thread("worker thread");
+ ASSERT_TRUE(worker_thread.Start());
+
+ CancelableTaskTracker::TaskId task_id =
+ task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(),
+ FROM_HERE,
+ Bind(&DoNothing),
+ MakeExpectedNotRunClosure(FROM_HERE));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ task_tracker_.TryCancel(task_id);
+
+ worker_thread.Stop();
+}
+
+void ExpectIsCanceled(
+ const CancelableTaskTracker::IsCanceledCallback& is_canceled,
+ bool expected_is_canceled) {
+ EXPECT_EQ(expected_is_canceled, is_canceled.Run());
+}
+
+// Create a new task ID and check its status on a separate thread
+// before and after canceling. The is-canceled callback should be
+// thread-safe (i.e., nothing should blow up).
+TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) {
+ CancelableTaskTracker::IsCanceledCallback is_canceled;
+ CancelableTaskTracker::TaskId task_id =
+ task_tracker_.NewTrackedTaskId(&is_canceled);
+
+ EXPECT_FALSE(is_canceled.Run());
+
+ Thread other_thread("other thread");
+ ASSERT_TRUE(other_thread.Start());
+ other_thread.message_loop_proxy()->PostTask(
+ FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, false));
+ other_thread.Stop();
+
+ task_tracker_.TryCancel(task_id);
+
+ ASSERT_TRUE(other_thread.Start());
+ other_thread.message_loop_proxy()->PostTask(
+ FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true));
+ other_thread.Stop();
+}
+
+// With the task tracker, post a task, a task with a reply, get a new
+// task id, and then cancel all of them. None of the tasks nor the
+// reply should run and the "is canceled" callback should return
+// true.
+TEST_F(CancelableTaskTrackerTest, CancelAll) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ ignore_result(task_tracker_.PostTask(
+ test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
+
+ ignore_result(
+ task_tracker_.PostTaskAndReply(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedNotRunClosure(FROM_HERE),
+ MakeExpectedNotRunClosure(FROM_HERE)));
+
+ CancelableTaskTracker::IsCanceledCallback is_canceled;
+ ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
+
+ task_tracker_.TryCancelAll();
+
+ test_task_runner->RunUntilIdle();
+
+ RunCurrentLoopUntilIdle();
+
+ EXPECT_TRUE(is_canceled.Run());
+}
+
+// With the task tracker, post a task, a task with a reply, get a new
+// task id, and then cancel all of them. None of the tasks nor the
+// reply should run and the "is canceled" callback should return
+// true.
+TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ CancelableTaskTracker::IsCanceledCallback is_canceled;
+
+ {
+ // Create another task tracker with a smaller scope.
+ CancelableTaskTracker task_tracker;
+
+ ignore_result(task_tracker.PostTask(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedNotRunClosure(FROM_HERE)));
+
+ ignore_result(
+ task_tracker.PostTaskAndReply(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedNotRunClosure(FROM_HERE),
+ MakeExpectedNotRunClosure(FROM_HERE)));
+
+ ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
+ }
+
+ test_task_runner->RunUntilIdle();
+
+ RunCurrentLoopUntilIdle();
+
+ EXPECT_FALSE(is_canceled.Run());
+}
+
+// Post a task and cancel it. HasTrackedTasks() should return true
+// from when the task is posted until the (do-nothing) reply task is
+// flushed.
+TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+
+ ignore_result(task_tracker_.PostTask(
+ test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE)));
+
+ task_tracker_.TryCancelAll();
+
+ test_task_runner->RunUntilIdle();
+
+ EXPECT_TRUE(task_tracker_.HasTrackedTasks());
+
+ RunCurrentLoopUntilIdle();
+
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+}
+
+// Post a task with a reply and cancel it. HasTrackedTasks() should
+// return true from when the task is posted until it is canceled.
+TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReply) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+
+ ignore_result(
+ task_tracker_.PostTaskAndReply(test_task_runner.get(),
+ FROM_HERE,
+ MakeExpectedNotRunClosure(FROM_HERE),
+ MakeExpectedNotRunClosure(FROM_HERE)));
+
+ task_tracker_.TryCancelAll();
+
+ test_task_runner->RunUntilIdle();
+
+ EXPECT_TRUE(task_tracker_.HasTrackedTasks());
+
+ RunCurrentLoopUntilIdle();
+
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+}
+
+// Create a new tracked task ID. HasTrackedTasks() should return true
+// until the IsCanceledCallback is destroyed.
+TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelled) {
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+
+ CancelableTaskTracker::IsCanceledCallback is_canceled;
+ ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled));
+
+ task_tracker_.TryCancelAll();
+
+ EXPECT_TRUE(task_tracker_.HasTrackedTasks());
+
+ is_canceled.Reset();
+
+ EXPECT_FALSE(task_tracker_.HasTrackedTasks());
+}
+
+// The death tests below make sure that calling task tracker member
+// functions from a thread different from its owner thread DCHECKs in
+// debug mode.
+
+class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
+ protected:
+ CancelableTaskTrackerDeathTest() {
+ // The default style "fast" does not support multi-threaded tests.
+ ::testing::FLAGS_gtest_death_test_style = "threadsafe";
+ }
+
+ virtual ~CancelableTaskTrackerDeathTest() {}
+};
+
+// Duplicated from base/threading/thread_checker.h so that we can be
+// good citizens there and undef the macro.
+#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
+#define ENABLE_THREAD_CHECKER 1
+#else
+#define ENABLE_THREAD_CHECKER 0
+#endif
+
+// Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
+void MaybeRunDeadlyTaskTrackerMemberFunction(
+ CancelableTaskTracker* task_tracker,
+ const Callback<void(CancelableTaskTracker*)>& fn) {
+// CancelableTask uses DCHECKs with its ThreadChecker (itself only
+// enabled in debug mode).
+#if ENABLE_THREAD_CHECKER
+ EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), "");
+#endif
+}
+
+void PostDoNothingTask(CancelableTaskTracker* task_tracker) {
+ ignore_result(task_tracker->PostTask(
+ scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(),
+ FROM_HERE,
+ Bind(&DoNothing)));
+}
+
+TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) {
+ Thread bad_thread("bad thread");
+ ASSERT_TRUE(bad_thread.Start());
+
+ bad_thread.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
+ Unretained(&task_tracker_),
+ Bind(&PostDoNothingTask)));
+}
+
+void TryCancel(CancelableTaskTracker::TaskId task_id,
+ CancelableTaskTracker* task_tracker) {
+ task_tracker->TryCancel(task_id);
+}
+
+TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ Thread bad_thread("bad thread");
+ ASSERT_TRUE(bad_thread.Start());
+
+ CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
+ test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ bad_thread.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
+ Unretained(&task_tracker_),
+ Bind(&TryCancel, task_id)));
+
+ test_task_runner->RunUntilIdle();
+}
+
+TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) {
+ scoped_refptr<TestSimpleTaskRunner> test_task_runner(
+ new TestSimpleTaskRunner());
+
+ Thread bad_thread("bad thread");
+ ASSERT_TRUE(bad_thread.Start());
+
+ CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
+ test_task_runner.get(), FROM_HERE, Bind(&DoNothing));
+ EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
+
+ bad_thread.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ Bind(&MaybeRunDeadlyTaskTrackerMemberFunction,
+ Unretained(&task_tracker_),
+ Bind(&CancelableTaskTracker::TryCancelAll)));
+
+ test_task_runner->RunUntilIdle();
+}
+
+} // namespace base
diff --git a/chromium/base/task_runner_util_unittest.cc b/chromium/base/task_runner_util_unittest.cc
index 04743944d63..481f09ebd19 100644
--- a/chromium/base/task_runner_util_unittest.cc
+++ b/chromium/base/task_runner_util_unittest.cc
@@ -45,20 +45,20 @@ void ExpectFoo(scoped_ptr<Foo> foo) {
EXPECT_FALSE(foo.get());
}
-struct FreeFooFunctor {
+struct FooDeleter {
void operator()(Foo* foo) const {
++g_foo_free_count;
delete foo;
};
};
-scoped_ptr_malloc<Foo, FreeFooFunctor> CreateScopedFoo() {
- return scoped_ptr_malloc<Foo, FreeFooFunctor>(new Foo);
+scoped_ptr<Foo, FooDeleter> CreateScopedFoo() {
+ return scoped_ptr<Foo, FooDeleter>(new Foo);
}
-void ExpectScopedFoo(scoped_ptr_malloc<Foo, FreeFooFunctor> foo) {
+void ExpectScopedFoo(scoped_ptr<Foo, FooDeleter> foo) {
EXPECT_TRUE(foo.get());
- scoped_ptr_malloc<Foo, FreeFooFunctor> local_foo(foo.Pass());
+ scoped_ptr<Foo, FooDeleter> local_foo(foo.Pass());
EXPECT_TRUE(local_foo.get());
EXPECT_FALSE(foo.get());
}
diff --git a/chromium/base/third_party/dmg_fp/dtoa.cc b/chromium/base/third_party/dmg_fp/dtoa.cc
index b03ccff569f..4eb9f0efd94 100644
--- a/chromium/base/third_party/dmg_fp/dtoa.cc
+++ b/chromium/base/third_party/dmg_fp/dtoa.cc
@@ -179,12 +179,6 @@
* used for input more than STRTOD_DIGLIM digits long (default 40).
*/
-#if defined _MSC_VER && _MSC_VER == 1800
-// TODO(scottmg): VS2013 RC ICEs on a bunch of functions in this file.
-// This should be removed after RTM. See http://crbug.com/288948.
-#pragma optimize("", off)
-#endif
-
#define IEEE_8087
#define NO_HEX_FP
diff --git a/chromium/base/third_party/dynamic_annotations/BUILD.gn b/chromium/base/third_party/dynamic_annotations/BUILD.gn
new file mode 100644
index 00000000000..fdaf44d3b3d
--- /dev/null
+++ b/chromium/base/third_party/dynamic_annotations/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+source_set("dynamic_annotations") {
+ sources = [
+ "dynamic_annotations.c",
+ "dynamic_annotations.h",
+ "../valgrind/valgrind.h",
+ ]
+}
diff --git a/chromium/base/third_party/nspr/BUILD.gn b/chromium/base/third_party/nspr/BUILD.gn
new file mode 100644
index 00000000000..eb758d500b8
--- /dev/null
+++ b/chromium/base/third_party/nspr/BUILD.gn
@@ -0,0 +1,15 @@
+# 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.
+
+source_set("nspr") {
+ visibility = "//base/*"
+ sources = [
+ "prtime.cc",
+ "prtime.h",
+ ]
+
+ # In GYP this project is part of base, so it uses the base implementation
+ # define. TODO(brettw) rename this define.
+ defines = [ "BASE_IMPLEMENTATION" ]
+}
diff --git a/chromium/base/third_party/nspr/nspr.gyp b/chromium/base/third_party/nspr/nspr.gyp
deleted file mode 100644
index 681e6ae9075..00000000000
--- a/chromium/base/third_party/nspr/nspr.gyp
+++ /dev/null
@@ -1,41 +0,0 @@
-# Copyright (c) 2012 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-{
- 'conditions': [
- ['use_system_nspr==1', {
- 'targets': [
- {
- 'target_name': 'nspr',
- 'type': 'none',
- 'toolsets': ['host', 'target'],
- 'variables': {
- 'headers_root_path': '.',
- 'header_filenames': [
- 'prcpucfg.h',
- 'prtime.h',
- 'prtypes.h',
- ],
- },
- 'includes': [
- '../../../build/shim_headers.gypi',
- ],
- 'direct_dependent_settings': {
- 'cflags': [
- '<!@(pkg-config --cflags nspr)',
- ],
- },
- 'link_settings': {
- 'ldflags': [
- '<!@(pkg-config --libs-only-L --libs-only-other nspr)',
- ],
- 'libraries': [
- '<!@(pkg-config --libs-only-l nspr)',
- ],
- },
- }
- ],
- }],
- ],
-}
diff --git a/chromium/base/third_party/nspr/prcpucfg.h b/chromium/base/third_party/nspr/prcpucfg.h
deleted file mode 100644
index d6ebb47abd0..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg.h
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright 2008, Google Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
-#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
-
-#if defined(WIN32)
-#include "base/third_party/nspr/prcpucfg_win.h"
-#elif defined(__APPLE__)
-#include "base/third_party/nspr/prcpucfg_mac.h"
-#elif defined(__native_client__)
-#include "base/third_party/nspr/prcpucfg_nacl.h"
-#elif defined(__linux__) || defined(ANDROID)
-#include "base/third_party/nspr/prcpucfg_linux.h"
-#elif defined(__FreeBSD__)
-#include "base/third_party/nspr/prcpucfg_freebsd.h"
-#elif defined(__OpenBSD__)
-#include "base/third_party/nspr/prcpucfg_openbsd.h"
-#elif defined(__sun)
-#include "base/third_party/nspr/prcpucfg_solaris.h"
-#else
-#error Provide a prcpucfg.h appropriate for your platform
-#endif
-
-#endif // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
diff --git a/chromium/base/third_party/nspr/prcpucfg_freebsd.h b/chromium/base/third_party/nspr/prcpucfg_freebsd.h
deleted file mode 100644
index 76d35420271..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_freebsd.h
+++ /dev/null
@@ -1,337 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#ifndef FREEBSD
-#define FREEBSD
-#endif
-
-#define PR_AF_INET6 28 /* same as AF_INET6 */
-
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG
-#endif
-
-#if defined(__i386__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#undef HAVE_ALIGNED_DOUBLES
-#undef HAVE_ALIGNED_LONGLONGS
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-
-#elif defined(__alpha__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#elif defined(__sparc__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#elif defined(__ia64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#elif defined(__amd64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#else
-
-#error "Unknown CPU architecture"
-
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prcpucfg_linux.h b/chromium/base/third_party/nspr/prcpucfg_linux.h
deleted file mode 100644
index e4336da9e89..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_linux.h
+++ /dev/null
@@ -1,756 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#ifndef LINUX
-#define LINUX
-#endif
-
-#define PR_AF_INET6 10 /* same as AF_INET6 */
-
-#ifdef __powerpc64__
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__powerpc__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__alpha)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__ia64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__x86_64__)
-
-#ifdef __ILP32__
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#else
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#endif
-
-#elif defined(__mc68000__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 2
-#define PR_ALIGN_OF_LONG 2
-#define PR_ALIGN_OF_INT64 2
-#define PR_ALIGN_OF_FLOAT 2
-#define PR_ALIGN_OF_DOUBLE 2
-#define PR_ALIGN_OF_POINTER 2
-#define PR_ALIGN_OF_WORD 2
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__sparc__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__i386__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__mips__)
-
-#ifdef __MIPSEB__
-#define IS_BIG_ENDIAN 1
-#undef IS_LITTLE_ENDIAN
-#elif defined(__MIPSEL__)
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#else
-#error "Unknown MIPS endianness."
-#endif
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__arm__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__hppa__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__s390x__)
-
-#define IS_BIG_ENDIAN 1
-#undef IS_LITTLE_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__s390__)
-
-#define IS_BIG_ENDIAN 1
-#undef IS_LITTLE_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#else
-
-#error "Unknown CPU architecture"
-
-#endif
-
-#define HAVE_LONG_LONG
-#if PR_ALIGN_OF_DOUBLE == 8
-#define HAVE_ALIGNED_DOUBLES
-#endif
-#if PR_ALIGN_OF_INT64 == 8
-#define HAVE_ALIGNED_LONGLONGS
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prcpucfg_mac.h b/chromium/base/third_party/nspr/prcpucfg_mac.h
deleted file mode 100644
index 60bea8e5c38..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_mac.h
+++ /dev/null
@@ -1,197 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#define PR_AF_INET6 30 /* same as AF_INET6 */
-
-#ifdef __LITTLE_ENDIAN__
-#undef IS_BIG_ENDIAN
-#define IS_LITTLE_ENDIAN 1
-#else
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-#endif
-
-#ifdef __x86_64__
-#define IS_64
-#endif
-
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG
-#endif
-#undef HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS 1
-
-#ifdef IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-#define PR_BITS_PER_DWORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-#define PR_BITS_PER_DWORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-#define PR_ALIGN_OF_DWORD 8
-
-#else /* IS_64 */
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-#define PR_BITS_PER_DWORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#endif /* IS_64 */
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
-
diff --git a/chromium/base/third_party/nspr/prcpucfg_nacl.h b/chromium/base/third_party/nspr/prcpucfg_nacl.h
deleted file mode 100644
index d8602d35111..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_nacl.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#ifndef LINUX
-#define LINUX
-#endif
-
-#define PR_AF_INET6 10 /* same as AF_INET6 */
-
-#if defined(__x86_64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__i386__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(__arm__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-#define PR_ALIGN_OF_WORD 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#else
-
-#error "Unknown CPU architecture"
-
-#endif
-
-#define HAVE_LONG_LONG
-#if PR_ALIGN_OF_DOUBLE == 8
-#define HAVE_ALIGNED_DOUBLES
-#endif
-#if PR_ALIGN_OF_INT64 == 8
-#define HAVE_ALIGNED_LONGLONGS
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prcpucfg_openbsd.h b/chromium/base/third_party/nspr/prcpucfg_openbsd.h
deleted file mode 100644
index 93c5b32bc53..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_openbsd.h
+++ /dev/null
@@ -1,337 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#ifndef OPENBSD
-#define OPENBSD
-#endif
-
-#define PR_AF_INET6 28 /* same as AF_INET6 */
-
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG
-#endif
-
-#if defined(__i386__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#undef HAVE_ALIGNED_DOUBLES
-#undef HAVE_ALIGNED_LONGLONGS
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-
-#elif defined(__alpha__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#elif defined(__sparc__)
-
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#elif defined(__ia64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#elif defined(__amd64__)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-#define PR_ALIGN_OF_WORD 8
-
-#else
-
-#error "Unknown CPU architecture"
-
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prcpucfg_solaris.h b/chromium/base/third_party/nspr/prcpucfg_solaris.h
deleted file mode 100644
index 81313e553db..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_solaris.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_UNIX
-#define XP_UNIX
-#endif
-
-#ifndef SOLARIS
-#define SOLARIS
-#endif
-
-#define PR_AF_INET6 26 /* same as AF_INET6 */
-
-#if defined(sparc) || defined(__sparc)
-#undef IS_LITTLE_ENDIAN
-#define IS_BIG_ENDIAN 1
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_DOUBLE 8
-#if defined(__sparcv9)
-#define IS_64
-#endif
-#elif defined(__x86_64)
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_DOUBLE 8
-#define IS_64
-#elif defined(i386) || defined(__i386)
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define PR_ALIGN_OF_INT64 4
-#define PR_ALIGN_OF_DOUBLE 4
-#else
-#error unknown processor
-#endif
-
-#ifdef IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 8
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 64
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 6
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_POINTER 8
-
-#else /* IS_64 */
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_DOUBLE 8
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_DOUBLE 64
-#define PR_BITS_PER_WORD 32
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_DOUBLE_LOG2 6
-#define PR_BITS_PER_WORD_LOG2 5
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_POINTER 4
-
-#endif /* IS_64 */
-
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG
-#endif
-#define HAVE_ALIGNED_DOUBLES
-#define HAVE_ALIGNED_LONGLONGS
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* ifndef nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prcpucfg_win.h b/chromium/base/third_party/nspr/prcpucfg_win.h
deleted file mode 100644
index 4ad534580a9..00000000000
--- a/chromium/base/third_party/nspr/prcpucfg_win.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-#ifndef nspr_cpucfg___
-#define nspr_cpucfg___
-
-#ifndef XP_PC
-#define XP_PC
-#endif
-
-#ifndef WIN32
-#define WIN32
-#endif
-
-#ifndef WIN95
-#define WIN95
-#endif
-
-#define PR_AF_INET6 23 /* same as AF_INET6 */
-
-#if defined(_M_IX86) || defined(_X86_)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_WORD 4
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_DOUBLE 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_WORD 32
-#define PR_BITS_PER_DWORD 64
-#define PR_BITS_PER_DOUBLE 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_WORD_LOG2 5
-#define PR_BITS_PER_DWORD_LOG2 6
-#define PR_BITS_PER_DOUBLE_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_WORD 4
-#define PR_ALIGN_OF_DWORD 8
-#define PR_ALIGN_OF_DOUBLE 4
-#define PR_ALIGN_OF_POINTER 4
-
-#define PR_BYTES_PER_WORD_LOG2 2
-#define PR_BYTES_PER_DWORD_LOG2 2
-
-#elif defined(_M_X64) || defined(_M_AMD64) || defined(_AMD64_)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_DOUBLE 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_WORD 64
-#define PR_BITS_PER_DWORD 64
-#define PR_BITS_PER_DOUBLE 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_WORD_LOG2 6
-#define PR_BITS_PER_DWORD_LOG2 6
-#define PR_BITS_PER_DOUBLE_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_WORD 8
-#define PR_ALIGN_OF_DWORD 8
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#elif defined(_M_IA64) || defined(_IA64_)
-
-#define IS_LITTLE_ENDIAN 1
-#undef IS_BIG_ENDIAN
-#define IS_64
-
-#define PR_BYTES_PER_BYTE 1
-#define PR_BYTES_PER_SHORT 2
-#define PR_BYTES_PER_INT 4
-#define PR_BYTES_PER_INT64 8
-#define PR_BYTES_PER_LONG 4
-#define PR_BYTES_PER_FLOAT 4
-#define PR_BYTES_PER_WORD 8
-#define PR_BYTES_PER_DWORD 8
-#define PR_BYTES_PER_DOUBLE 8
-
-#define PR_BITS_PER_BYTE 8
-#define PR_BITS_PER_SHORT 16
-#define PR_BITS_PER_INT 32
-#define PR_BITS_PER_INT64 64
-#define PR_BITS_PER_LONG 32
-#define PR_BITS_PER_FLOAT 32
-#define PR_BITS_PER_WORD 64
-#define PR_BITS_PER_DWORD 64
-#define PR_BITS_PER_DOUBLE 64
-
-#define PR_BITS_PER_BYTE_LOG2 3
-#define PR_BITS_PER_SHORT_LOG2 4
-#define PR_BITS_PER_INT_LOG2 5
-#define PR_BITS_PER_INT64_LOG2 6
-#define PR_BITS_PER_LONG_LOG2 5
-#define PR_BITS_PER_FLOAT_LOG2 5
-#define PR_BITS_PER_WORD_LOG2 6
-#define PR_BITS_PER_DWORD_LOG2 6
-#define PR_BITS_PER_DOUBLE_LOG2 6
-
-#define PR_ALIGN_OF_SHORT 2
-#define PR_ALIGN_OF_INT 4
-#define PR_ALIGN_OF_LONG 4
-#define PR_ALIGN_OF_INT64 8
-#define PR_ALIGN_OF_FLOAT 4
-#define PR_ALIGN_OF_WORD 8
-#define PR_ALIGN_OF_DWORD 8
-#define PR_ALIGN_OF_DOUBLE 8
-#define PR_ALIGN_OF_POINTER 8
-
-#define PR_BYTES_PER_WORD_LOG2 3
-#define PR_BYTES_PER_DWORD_LOG2 3
-
-#else /* defined(_M_IX86) || defined(_X86_) */
-
-#error unknown processor architecture
-
-#endif /* defined(_M_IX86) || defined(_X86_) */
-
-#ifndef HAVE_LONG_LONG
-#define HAVE_LONG_LONG
-#endif
-
-#ifndef NO_NSPR_10_SUPPORT
-
-#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
-#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
-#define BYTES_PER_INT PR_BYTES_PER_INT
-#define BYTES_PER_INT64 PR_BYTES_PER_INT64
-#define BYTES_PER_LONG PR_BYTES_PER_LONG
-#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
-#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
-#define BYTES_PER_WORD PR_BYTES_PER_WORD
-#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
-
-#define BITS_PER_BYTE PR_BITS_PER_BYTE
-#define BITS_PER_SHORT PR_BITS_PER_SHORT
-#define BITS_PER_INT PR_BITS_PER_INT
-#define BITS_PER_INT64 PR_BITS_PER_INT64
-#define BITS_PER_LONG PR_BITS_PER_LONG
-#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
-#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
-#define BITS_PER_WORD PR_BITS_PER_WORD
-
-#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
-#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
-#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
-#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
-#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
-#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
-#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
-#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
-
-#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
-#define ALIGN_OF_INT PR_ALIGN_OF_INT
-#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
-#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
-#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
-#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
-#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
-#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
-
-#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
-#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
-#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
-
-#endif /* NO_NSPR_10_SUPPORT */
-
-#endif /* nspr_cpucfg___ */
diff --git a/chromium/base/third_party/nspr/prtime.cc b/chromium/base/third_party/nspr/prtime.cc
index 5fc89c5e449..2029660c6f7 100644
--- a/chromium/base/third_party/nspr/prtime.cc
+++ b/chromium/base/third_party/nspr/prtime.cc
@@ -61,6 +61,8 @@
* 1. prtime.h
* 2. prtypes.h
* 3. prlong.h
+ *
+ * Unit tests are in base/time/pr_time_unittest.cc.
*/
#include "base/logging.h"
@@ -132,6 +134,8 @@ PR_ImplodeTime(const PRExplodedTime *exploded)
// Adjust for time zone and dst. Convert from seconds to microseconds.
result -= (exploded->tm_params.tp_gmt_offset +
exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds;
+ // Add microseconds that cannot be represented in |st|.
+ result += exploded->tm_usec % 1000;
return result;
#elif defined(OS_MACOSX)
// Create the system struct representing our exploded time.
@@ -443,10 +447,6 @@ PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params)
PRTimeParameters
PR_GMTParameters(const PRExplodedTime *gmt)
{
-#if defined(XP_MAC)
-#pragma unused (gmt)
-#endif
-
PRTimeParameters retVal = { 0, 0 };
return retVal;
}
@@ -509,6 +509,7 @@ typedef enum
* 06/21/95 04:24:34 PM
* 20/06/95 21:07
* 95-06-08 19:32:48 EDT
+ * 1995-06-17T23:11:25.342156Z
*
* If the input string doesn't contain a description of the timezone,
* we consult the `default_to_gmt' to decide whether the string should
@@ -535,6 +536,7 @@ PR_ParseTimeString(
int hour = -1;
int min = -1;
int sec = -1;
+ int usec = -1;
const char *rest = string;
@@ -778,6 +780,7 @@ PR_ParseTimeString(
int tmp_hour = -1;
int tmp_min = -1;
int tmp_sec = -1;
+ int tmp_usec = -1;
const char *end = rest + 1;
while (*end >= '0' && *end <= '9')
end++;
@@ -837,14 +840,38 @@ PR_ParseTimeString(
else
tmp_sec = (rest[0]-'0');
- /* If we made it here, we've parsed hour and min,
- and possibly sec, so it worked as a unit. */
+ /* fractional second */
+ rest = end;
+ if (*rest == '.')
+ {
+ rest++;
+ end++;
+ tmp_usec = 0;
+ /* use up to 6 digits, skip over the rest */
+ while (*end >= '0' && *end <= '9')
+ {
+ if (end - rest < 6)
+ tmp_usec = tmp_usec * 10 + *end - '0';
+ end++;
+ }
+ int ndigits = end - rest;
+ while (ndigits++ < 6)
+ tmp_usec *= 10;
+ rest = end;
+ }
- /* skip over whitespace and see if there's an AM or PM
- directly following the time.
- */
- if (tmp_hour <= 12)
+ if (*rest == 'Z')
{
+ zone = TT_GMT;
+ rest++;
+ }
+ else if (tmp_hour <= 12)
+ {
+ /* If we made it here, we've parsed hour and min,
+ and possibly sec, so the current token is a time.
+ Now skip over whitespace and see if there's an AM
+ or PM directly following the time.
+ */
const char *s = end;
while (*s && (*s == ' ' || *s == '\t'))
s++;
@@ -862,6 +889,7 @@ PR_ParseTimeString(
hour = tmp_hour;
min = tmp_min;
sec = tmp_sec;
+ usec = tmp_usec;
rest = end;
break;
}
@@ -869,8 +897,7 @@ PR_ParseTimeString(
end[1] >= '0' && end[1] <= '9')
{
/* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
- or even 95-06-05...
- #### But it doesn't handle 1995-06-22.
+ or even 95-06-05 or 1995-06-22.
*/
int n1, n2, n3;
const char *s;
@@ -881,9 +908,19 @@ PR_ParseTimeString(
s = rest;
- n1 = (*s++ - '0'); /* first 1 or 2 digits */
+ n1 = (*s++ - '0'); /* first 1, 2 or 4 digits */
if (*s >= '0' && *s <= '9')
- n1 = n1*10 + (*s++ - '0');
+ {
+ n1 = n1*10 + (*s++ - '0');
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3 and 4 */
+ {
+ n1 = n1*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9')
+ break;
+ n1 = n1*10 + (*s++ - '0');
+ }
+ }
if (*s != '/' && *s != '-') /* slash */
break;
@@ -915,17 +952,21 @@ PR_ParseTimeString(
n3 = n3*10 + (*s++ - '0');
}
- if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
- (*s >= 'A' && *s <= 'Z') ||
- (*s >= 'a' && *s <= 'z'))
+ if (*s == 'T' && s[1] >= '0' && s[1] <= '9')
+ /* followed by ISO 8601 T delimiter and number is ok */
+ ;
+ else if ((*s >= '0' && *s <= '9') ||
+ (*s >= 'A' && *s <= 'Z') ||
+ (*s >= 'a' && *s <= 'z'))
+ /* but other alphanumerics are not ok */
break;
- /* Ok, we parsed three 1-2 digit numbers, with / or -
+ /* Ok, we parsed three multi-digit numbers, with / or -
between them. Now decide what the hell they are
- (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+ (DD/MM/YY or MM/DD/YY or [YY]YY/MM/DD.)
*/
- if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
+ if (n1 > 31 || n1 == 0) /* must be [YY]YY/MM/DD */
{
if (n2 > 12) break;
if (n3 > 31) break;
@@ -1018,26 +1059,27 @@ PR_ParseTimeString(
/* else, three or more than five digits - what's that? */
break;
- }
- }
+ } /* case '0' .. '9' */
+ } /* switch */
/* Skip to the end of this token, whether we parsed it or not.
- Tokens are delimited by whitespace, or ,;-/
- But explicitly not :+-.
+ Tokens are delimited by whitespace, or ,;-+/()[] but explicitly not .:
+ 'T' is also treated as delimiter when followed by a digit (ISO 8601).
*/
while (*rest &&
*rest != ' ' && *rest != '\t' &&
*rest != ',' && *rest != ';' &&
*rest != '-' && *rest != '+' &&
*rest != '/' &&
- *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
+ *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']' &&
+ !(*rest == 'T' && rest[1] >= '0' && rest[1] <= '9')
+ )
rest++;
/* skip over uninteresting chars. */
SKIP_MORE:
- while (*rest &&
- (*rest == ' ' || *rest == '\t' ||
- *rest == ',' || *rest == ';' || *rest == '/' ||
- *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
+ while (*rest == ' ' || *rest == '\t' ||
+ *rest == ',' || *rest == ';' || *rest == '/' ||
+ *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')
rest++;
/* "-" is ignored at the beginning of a token if we have not yet
@@ -1051,7 +1093,10 @@ PR_ParseTimeString(
goto SKIP_MORE;
}
- }
+ /* Skip T that may precede ISO 8601 time. */
+ if (*rest == 'T' && rest[1] >= '0' && rest[1] <= '9')
+ rest++;
+ } /* while */
if (zone != TT_UNKNOWN && zone_offset == -1)
{
@@ -1086,6 +1131,8 @@ PR_ParseTimeString(
return PR_FAILURE;
memset(result, 0, sizeof(*result));
+ if (usec != -1)
+ result->tm_usec = usec;
if (sec != -1)
result->tm_sec = sec;
if (min != -1)
@@ -1139,11 +1186,9 @@ PR_ParseTimeString(
*/
/* month, day, hours, mins and secs are always non-negative
- so we dont need to worry about them. */
- if(result->tm_year >= 1970)
+ so we dont need to worry about them. */
+ if (result->tm_year >= 1970)
{
- PRInt64 usec_per_sec;
-
localTime.tm_sec = result->tm_sec;
localTime.tm_min = result->tm_min;
localTime.tm_hour = result->tm_hour;
@@ -1183,11 +1228,8 @@ PR_ParseTimeString(
#endif
if (secs != (time_t) -1)
{
- PRTime usecs64;
- LL_I2L(usecs64, secs);
- LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
- LL_MUL(usecs64, usecs64, usec_per_sec);
- *result_imploded = usecs64;
+ *result_imploded = (PRInt64)secs * PR_USEC_PER_SEC;
+ *result_imploded += result->tm_usec;
return PR_SUCCESS;
}
}
diff --git a/chromium/base/third_party/nspr/prtime.h b/chromium/base/third_party/nspr/prtime.h
index ffbedec8253..01a4e540782 100644
--- a/chromium/base/third_party/nspr/prtime.h
+++ b/chromium/base/third_party/nspr/prtime.h
@@ -52,13 +52,26 @@
#ifndef BASE_PRTIME_H__
#define BASE_PRTIME_H__
+#include <stdint.h>
+
#include "base/base_export.h"
-#include "base/third_party/nspr/prtypes.h"
-#define PR_ASSERT DCHECK
+typedef int8_t PRInt8;
+typedef int16_t PRInt16;
+typedef int32_t PRInt32;
+typedef int64_t PRInt64;
+typedef int PRIntn;
+
+typedef PRIntn PRBool;
+#define PR_TRUE 1
+#define PR_FALSE 0
-#define LL_I2L(l, i) ((l) = (PRInt64)(i))
-#define LL_MUL(r, a, b) ((r) = (a) * (b))
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#define PR_ASSERT DCHECK
+#define PR_CALLBACK
+#define PR_INT16_MAX 32767
+#define NSPR_API(__type) extern __type
/**********************************************************************/
/************************* TYPES AND CONSTANTS ************************/
@@ -217,6 +230,7 @@ NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt);
* 06/21/95 04:24:34 PM
* 20/06/95 21:07
* 95-06-08 19:32:48 EDT
+ * 1995-06-17T23:11:25.342156Z
*
* If the input string doesn't contain a description of the timezone,
* we consult the `default_to_gmt' to decide whether the string should
diff --git a/chromium/base/third_party/nspr/prtypes.h b/chromium/base/third_party/nspr/prtypes.h
deleted file mode 100644
index 630df81a761..00000000000
--- a/chromium/base/third_party/nspr/prtypes.h
+++ /dev/null
@@ -1,558 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape Portable Runtime (NSPR).
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1998-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
-** File: prtypes.h
-** Description: Definitions of NSPR's basic types
-**
-** Prototypes and macros used to make up for deficiencies that we have found
-** in ANSI environments.
-**
-** Since we do not wrap <stdlib.h> and all the other standard headers, authors
-** of portable code will not know in general that they need these definitions.
-** Instead of requiring these authors to find the dependent uses in their code
-** and take the following steps only in those C files, we take steps once here
-** for all C files.
-**/
-
-#ifndef prtypes_h___
-#define prtypes_h___
-
-#ifdef MDCPUCFG
-#include MDCPUCFG
-#else
-#include "base/third_party/nspr/prcpucfg.h"
-#endif
-
-#include <stddef.h>
-
-/***********************************************************************
-** MACROS: PR_EXTERN
-** PR_IMPLEMENT
-** DESCRIPTION:
-** These are only for externally visible routines and globals. For
-** internal routines, just use "extern" for type checking and that
-** will not export internal cross-file or forward-declared symbols.
-** Define a macro for declaring procedures return types. We use this to
-** deal with windoze specific type hackery for DLL definitions. Use
-** PR_EXTERN when the prototype for the method is declared. Use
-** PR_IMPLEMENT for the implementation of the method.
-**
-** Example:
-** in dowhim.h
-** PR_EXTERN( void ) DoWhatIMean( void );
-** in dowhim.c
-** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
-**
-**
-***********************************************************************/
-#if 1
-
-/*
-** Local change: the portions of NSPR used by the base module are
-** implementation details. NSPR symbols do not need to be exported beyond
-** the base module. For all platforms, avoid decorating functions with
-** specific visibility and access keywords.
-*/
-
-#define PR_EXPORT(__type) extern __type
-#define PR_EXPORT_DATA(__type) extern __type
-#define PR_IMPORT(__type) extern __type
-#define PR_IMPORT_DATA(__type) extern __type
-
-#define PR_EXTERN(__type) extern __type
-#define PR_IMPLEMENT(__type) __type
-#define PR_EXTERN_DATA(__type) extern __type
-#define PR_IMPLEMENT_DATA(__type) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(WIN32)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT(__type) __declspec(dllimport) __type
-#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
-
-#define PR_EXTERN(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(XP_BEOS)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
-
-#define PR_EXTERN(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(XP_OS2) && defined(__declspec)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT(__type) extern __declspec(dllimport) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
-
-#define PR_EXTERN(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
-#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
-#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#elif defined(SYMBIAN)
-
-#define PR_EXPORT(__type) extern __declspec(dllexport) __type
-#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
-#ifdef __WINS__
-#define PR_IMPORT(__type) extern __declspec(dllexport) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
-#else
-#define PR_IMPORT(__type) extern __declspec(dllimport) __type
-#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
-#endif
-
-#define PR_EXTERN(__type) extern __type
-#define PR_IMPLEMENT(__type) __type
-#define PR_EXTERN_DATA(__type) extern __type
-#define PR_IMPLEMENT_DATA(__type) __type
-
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#else /* Unix */
-
-/* GCC 3.3 and later support the visibility attribute. */
-#if (__GNUC__ >= 4) || \
- (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
-#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
-#else
-#define PR_VISIBILITY_DEFAULT
-#endif
-
-#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-
-#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
-#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
-#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
-#define PR_CALLBACK
-#define PR_CALLBACK_DECL
-#define PR_STATIC_CALLBACK(__x) static __x
-
-#endif
-
-#if defined(_NSPR_BUILD_)
-#define NSPR_API(__type) PR_EXPORT(__type)
-#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
-#else
-#define NSPR_API(__type) PR_IMPORT(__type)
-#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
-#endif
-
-/***********************************************************************
-** MACROS: PR_BEGIN_MACRO
-** PR_END_MACRO
-** DESCRIPTION:
-** Macro body brackets so that macros with compound statement definitions
-** behave syntactically more like functions when called.
-***********************************************************************/
-#define PR_BEGIN_MACRO do {
-#define PR_END_MACRO } while (0)
-
-/***********************************************************************
-** MACROS: PR_BEGIN_EXTERN_C
-** PR_END_EXTERN_C
-** DESCRIPTION:
-** Macro shorthands for conditional C++ extern block delimiters.
-***********************************************************************/
-#ifdef __cplusplus
-#define PR_BEGIN_EXTERN_C extern "C" {
-#define PR_END_EXTERN_C }
-#else
-#define PR_BEGIN_EXTERN_C
-#define PR_END_EXTERN_C
-#endif
-
-/***********************************************************************
-** MACROS: PR_BIT
-** PR_BITMASK
-** DESCRIPTION:
-** Bit masking macros. XXX n must be <= 31 to be portable
-***********************************************************************/
-#define PR_BIT(n) ((PRUint32)1 << (n))
-#define PR_BITMASK(n) (PR_BIT(n) - 1)
-
-/***********************************************************************
-** MACROS: PR_ROUNDUP
-** PR_MIN
-** PR_MAX
-** PR_ABS
-** DESCRIPTION:
-** Commonly used macros for operations on compatible types.
-***********************************************************************/
-#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
-#define PR_MIN(x,y) ((x)<(y)?(x):(y))
-#define PR_MAX(x,y) ((x)>(y)?(x):(y))
-#define PR_ABS(x) ((x)<0?-(x):(x))
-
-PR_BEGIN_EXTERN_C
-
-/************************************************************************
-** TYPES: PRUint8
-** PRInt8
-** DESCRIPTION:
-** The int8 types are known to be 8 bits each. There is no type that
-** is equivalent to a plain "char".
-************************************************************************/
-#if PR_BYTES_PER_BYTE == 1
-typedef unsigned char PRUint8;
-/*
-** Some cfront-based C++ compilers do not like 'signed char' and
-** issue the warning message:
-** warning: "signed" not implemented (ignored)
-** For these compilers, we have to define PRInt8 as plain 'char'.
-** Make sure that plain 'char' is indeed signed under these compilers.
-*/
-#if (defined(HPUX) && defined(__cplusplus) \
- && !defined(__GNUC__) && __cplusplus < 199707L) \
- || (defined(SCO) && defined(__cplusplus) \
- && !defined(__GNUC__) && __cplusplus == 1L)
-typedef char PRInt8;
-#else
-typedef signed char PRInt8;
-#endif
-#else
-#error No suitable type for PRInt8/PRUint8
-#endif
-
-/************************************************************************
- * MACROS: PR_INT8_MAX
- * PR_INT8_MIN
- * PR_UINT8_MAX
- * DESCRIPTION:
- * The maximum and minimum values of a PRInt8 or PRUint8.
-************************************************************************/
-
-#define PR_INT8_MAX 127
-#define PR_INT8_MIN (-128)
-#define PR_UINT8_MAX 255U
-
-/************************************************************************
-** TYPES: PRUint16
-** PRInt16
-** DESCRIPTION:
-** The int16 types are known to be 16 bits each.
-************************************************************************/
-#if PR_BYTES_PER_SHORT == 2
-typedef unsigned short PRUint16;
-typedef short PRInt16;
-#else
-#error No suitable type for PRInt16/PRUint16
-#endif
-
-/************************************************************************
- * MACROS: PR_INT16_MAX
- * PR_INT16_MIN
- * PR_UINT16_MAX
- * DESCRIPTION:
- * The maximum and minimum values of a PRInt16 or PRUint16.
-************************************************************************/
-
-#define PR_INT16_MAX 32767
-#define PR_INT16_MIN (-32768)
-#define PR_UINT16_MAX 65535U
-
-/************************************************************************
-** TYPES: PRUint32
-** PRInt32
-** DESCRIPTION:
-** The int32 types are known to be 32 bits each.
-************************************************************************/
-#if PR_BYTES_PER_INT == 4
-typedef unsigned int PRUint32;
-typedef int PRInt32;
-#define PR_INT32(x) x
-#define PR_UINT32(x) x ## U
-#elif PR_BYTES_PER_LONG == 4
-typedef unsigned long PRUint32;
-typedef long PRInt32;
-#define PR_INT32(x) x ## L
-#define PR_UINT32(x) x ## UL
-#else
-#error No suitable type for PRInt32/PRUint32
-#endif
-
-/************************************************************************
- * MACROS: PR_INT32_MAX
- * PR_INT32_MIN
- * PR_UINT32_MAX
- * DESCRIPTION:
- * The maximum and minimum values of a PRInt32 or PRUint32.
-************************************************************************/
-
-#define PR_INT32_MAX PR_INT32(2147483647)
-#define PR_INT32_MIN (-PR_INT32_MAX - 1)
-#define PR_UINT32_MAX PR_UINT32(4294967295)
-
-/************************************************************************
-** TYPES: PRUint64
-** PRInt64
-** DESCRIPTION:
-** The int64 types are known to be 64 bits each. Care must be used when
-** declaring variables of type PRUint64 or PRInt64. Different hardware
-** architectures and even different compilers have varying support for
-** 64 bit values. The only guaranteed portability requires the use of
-** the LL_ macros (see prlong.h).
-************************************************************************/
-#ifdef HAVE_LONG_LONG
-/* Keep this in sync with prlong.h. */
-/*
- * On 64-bit Mac OS X, uint64 needs to be defined as unsigned long long to
- * match uint64_t, otherwise our uint64 typedef conflicts with the uint64
- * typedef in cssmconfig.h, which CoreServices.h includes indirectly.
- */
-#if PR_BYTES_PER_LONG == 8 && !defined(__APPLE__)
-typedef long PRInt64;
-typedef unsigned long PRUint64;
-#elif defined(WIN32) && !defined(__GNUC__)
-typedef __int64 PRInt64;
-typedef unsigned __int64 PRUint64;
-#else
-typedef long long PRInt64;
-typedef unsigned long long PRUint64;
-#endif /* PR_BYTES_PER_LONG == 8 */
-#else /* !HAVE_LONG_LONG */
-typedef struct {
-#ifdef IS_LITTLE_ENDIAN
- PRUint32 lo, hi;
-#else
- PRUint32 hi, lo;
-#endif
-} PRInt64;
-typedef PRInt64 PRUint64;
-#endif /* !HAVE_LONG_LONG */
-
-/************************************************************************
-** TYPES: PRUintn
-** PRIntn
-** DESCRIPTION:
-** The PRIntn types are most appropriate for automatic variables. They are
-** guaranteed to be at least 16 bits, though various architectures may
-** define them to be wider (e.g., 32 or even 64 bits). These types are
-** never valid for fields of a structure.
-************************************************************************/
-#if PR_BYTES_PER_INT >= 2
-typedef int PRIntn;
-typedef unsigned int PRUintn;
-#else
-#error 'sizeof(int)' not sufficient for platform use
-#endif
-
-/************************************************************************
-** TYPES: PRFloat64
-** DESCRIPTION:
-** NSPR's floating point type is always 64 bits.
-************************************************************************/
-typedef double PRFloat64;
-
-/************************************************************************
-** TYPES: PRSize
-** DESCRIPTION:
-** A type for representing the size of objects.
-************************************************************************/
-typedef size_t PRSize;
-
-
-/************************************************************************
-** TYPES: PROffset32, PROffset64
-** DESCRIPTION:
-** A type for representing byte offsets from some location.
-************************************************************************/
-typedef PRInt32 PROffset32;
-typedef PRInt64 PROffset64;
-
-/************************************************************************
-** TYPES: PRPtrDiff
-** DESCRIPTION:
-** A type for pointer difference. Variables of this type are suitable
-** for storing a pointer or pointer subtraction.
-************************************************************************/
-typedef ptrdiff_t PRPtrdiff;
-
-/************************************************************************
-** TYPES: PRUptrdiff
-** DESCRIPTION:
-** A type for pointer difference. Variables of this type are suitable
-** for storing a pointer or pointer sutraction.
-************************************************************************/
-#ifdef _WIN64
-typedef PRUint64 PRUptrdiff;
-#else
-typedef unsigned long PRUptrdiff;
-#endif
-
-/************************************************************************
-** TYPES: PRBool
-** DESCRIPTION:
-** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
-** for clarity of target type in assignments and actual arguments. Use
-** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
-** just as you would C int-valued conditions.
-************************************************************************/
-typedef PRIntn PRBool;
-#define PR_TRUE 1
-#define PR_FALSE 0
-
-/************************************************************************
-** TYPES: PRPackedBool
-** DESCRIPTION:
-** Use PRPackedBool within structs where bitfields are not desirable
-** but minimum and consistant overhead matters.
-************************************************************************/
-typedef PRUint8 PRPackedBool;
-
-/*
-** Status code used by some routines that have a single point of failure or
-** special status return.
-*/
-typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
-
-#ifndef __PRUNICHAR__
-#define __PRUNICHAR__
-#ifdef WIN32
-typedef wchar_t PRUnichar;
-#else
-typedef PRUint16 PRUnichar;
-#endif
-#endif
-
-/*
-** WARNING: The undocumented data types PRWord and PRUword are
-** only used in the garbage collection and arena code. Do not
-** use PRWord and PRUword in new code.
-**
-** A PRWord is an integer that is the same size as a void*.
-** It implements the notion of a "word" in the Java Virtual
-** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine
-** Specification, Addison-Wesley, September 1996.
-** http://java.sun.com/docs/books/vmspec/index.html.)
-*/
-#ifdef _WIN64
-typedef PRInt64 PRWord;
-typedef PRUint64 PRUword;
-#else
-typedef long PRWord;
-typedef unsigned long PRUword;
-#endif
-
-#if defined(NO_NSPR_10_SUPPORT)
-#else
-/********* ???????????????? FIX ME ??????????????????????????? *****/
-/********************** Some old definitions until pr=>ds transition is done ***/
-/********************** Also, we are still using NSPR 1.0. GC ******************/
-/*
-** Fundamental NSPR macros, used nearly everywhere.
-*/
-
-#define PR_PUBLIC_API PR_IMPLEMENT
-
-/*
-** Macro body brackets so that macros with compound statement definitions
-** behave syntactically more like functions when called.
-*/
-#define NSPR_BEGIN_MACRO do {
-#define NSPR_END_MACRO } while (0)
-
-/*
-** Macro shorthands for conditional C++ extern block delimiters.
-*/
-#ifdef NSPR_BEGIN_EXTERN_C
-#undef NSPR_BEGIN_EXTERN_C
-#endif
-#ifdef NSPR_END_EXTERN_C
-#undef NSPR_END_EXTERN_C
-#endif
-
-#ifdef __cplusplus
-#define NSPR_BEGIN_EXTERN_C extern "C" {
-#define NSPR_END_EXTERN_C }
-#else
-#define NSPR_BEGIN_EXTERN_C
-#define NSPR_END_EXTERN_C
-#endif
-
-/********* ????????????? End Fix me ?????????????????????????????? *****/
-#endif /* NO_NSPR_10_SUPPORT */
-
-/*
-** Compile-time assert. "condition" must be a constant expression.
-** The macro can be used only in places where an "extern" declaration is
-** allowed.
-*/
-#define PR_STATIC_ASSERT(condition) \
- extern void pr_static_assert(int arg[(condition) ? 1 : -1])
-
-PR_END_EXTERN_C
-
-#if !defined(NO_NSPR_10_SUPPORT)
-#include "base/basictypes.h"
-#endif
-
-#endif /* prtypes_h___ */
-
diff --git a/chromium/base/third_party/superfasthash/LICENSE b/chromium/base/third_party/superfasthash/LICENSE
new file mode 100644
index 00000000000..3c40a3ecd79
--- /dev/null
+++ b/chromium/base/third_party/superfasthash/LICENSE
@@ -0,0 +1,27 @@
+Paul Hsieh OLD BSD license
+
+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.
diff --git a/chromium/base/third_party/superfasthash/OWNERS b/chromium/base/third_party/superfasthash/OWNERS
new file mode 100644
index 00000000000..f34cfb1c929
--- /dev/null
+++ b/chromium/base/third_party/superfasthash/OWNERS
@@ -0,0 +1,2 @@
+mgiuca@chromium.org
+rvargas@chromium.org
diff --git a/chromium/base/third_party/superfasthash/README.chromium b/chromium/base/third_party/superfasthash/README.chromium
new file mode 100644
index 00000000000..d41ed7724a2
--- /dev/null
+++ b/chromium/base/third_party/superfasthash/README.chromium
@@ -0,0 +1,29 @@
+Name: Paul Hsieh's SuperFastHash
+Short Name: SuperFastHash
+URL: http://www.azillionmonkeys.com/qed/hash.html
+Version: 0
+Date: 2012-02-21
+License: BSD
+License File: LICENSE
+Security Critical: yes
+
+Description:
+A fast string hashing algorithm.
+
+Local Modifications:
+- Added LICENSE.
+- Added license text as a comment to the top of superfasthash.c.
+- #include <stdint.h> instead of "pstdint.h".
+- #include <stdlib.h>.
+
+The license is a standard 3-clause BSD license with the following minor changes:
+
+"nor the names of its contributors may be used"
+is replaced with:
+"nor the names of any other contributors to the code use may not be used"
+
+and
+
+"IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE"
+is replaced with:
+"IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE"
diff --git a/chromium/base/third_party/superfasthash/superfasthash.c b/chromium/base/third_party/superfasthash/superfasthash.c
new file mode 100644
index 00000000000..6e7687e1319
--- /dev/null
+++ b/chromium/base/third_party/superfasthash/superfasthash.c
@@ -0,0 +1,84 @@
+// 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.
+
+#include <stdint.h>
+#include <stdlib.h>
+#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_t 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;
+ hash ^= ((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 += (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;
+}
diff --git a/chromium/base/third_party/symbolize/BUILD.gn b/chromium/base/third_party/symbolize/BUILD.gn
new file mode 100644
index 00000000000..0b4dd952bac
--- /dev/null
+++ b/chromium/base/third_party/symbolize/BUILD.gn
@@ -0,0 +1,20 @@
+# 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.
+
+source_set("symbolize") {
+ visibility = "//base/*"
+ sources = [
+ "config.h",
+ "demangle.cc",
+ "demangle.h",
+ "glog/logging.h",
+ "glog/raw_logging.h",
+ "symbolize.cc",
+ "symbolize.h",
+ "utilities.h",
+ ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+}
diff --git a/chromium/base/third_party/symbolize/README.chromium b/chromium/base/third_party/symbolize/README.chromium
index 520a3db5775..de92794c812 100644
--- a/chromium/base/third_party/symbolize/README.chromium
+++ b/chromium/base/third_party/symbolize/README.chromium
@@ -3,16 +3,13 @@ URL: http://code.google.com/p/google-glog/
License: BSD
The following files are copied AS-IS from:
-http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r76)
+http://code.google.com/p/google-glog/source/browse/#svn/trunk/src (r141)
- demangle.cc
- demangle.h
- symbolize.cc
- symbolize.h
-r137 (https://code.google.com/p/google-glog/source/detail?r=137) was merged
-after that.
-
The following files are minimal stubs created for use in Chromium:
- config.h
diff --git a/chromium/base/third_party/symbolize/demangle.cc b/chromium/base/third_party/symbolize/demangle.cc
index 46556bf3c13..e858181a68f 100644
--- a/chromium/base/third_party/symbolize/demangle.cc
+++ b/chromium/base/third_party/symbolize/demangle.cc
@@ -28,6 +28,11 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: Satoru Takabayashi
+//
+// For reference check out:
+// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling
+//
+// Note that we only have partial C++0x support yet.
#include <stdio.h> // for NULL
#include "demangle.h"
@@ -138,14 +143,12 @@ static const AbbrevPair kSubstitutionList[] = {
// State needed for demangling.
typedef struct {
const char *mangled_cur; // Cursor of mangled name.
- const char *mangled_end; // End of mangled name.
char *out_cur; // Cursor of output string.
const char *out_begin; // Beginning of output string.
const char *out_end; // End of output string.
const char *prev_name; // For constructors/destructors.
int prev_name_length; // For constructors/destructors.
- int nest_level; // For nested names.
- int number; // Remember the previous number.
+ short nest_level; // For nested names.
bool append; // Append flag.
bool overflowed; // True if output gets overflowed.
} State;
@@ -161,6 +164,16 @@ static size_t StrLen(const char *str) {
return len;
}
+// Returns true if "str" has at least "n" characters remaining.
+static bool AtLeastNumCharsRemaining(const char *str, int n) {
+ for (int i = 0; i < n; ++i) {
+ if (str[i] == '\0') {
+ return false;
+ }
+ }
+ return true;
+}
+
// Returns true if "str" has "prefix" as a prefix.
static bool StrPrefix(const char *str, const char *prefix) {
size_t i = 0;
@@ -174,39 +187,33 @@ static bool StrPrefix(const char *str, const char *prefix) {
static void InitState(State *state, const char *mangled,
char *out, int out_size) {
state->mangled_cur = mangled;
- state->mangled_end = mangled + StrLen(mangled);
state->out_cur = out;
state->out_begin = out;
state->out_end = out + out_size;
state->prev_name = NULL;
state->prev_name_length = -1;
state->nest_level = -1;
- state->number = -1;
state->append = true;
state->overflowed = false;
}
-// Calculates the remaining length of the mangled name.
-static int RemainingLength(State *state) {
- return state->mangled_end - state->mangled_cur;
-}
-
-// Returns true and advances "mangled_cur" if we find "c" at
-// "mangled_cur" position.
-static bool ParseChar(State *state, const char c) {
- if (RemainingLength(state) >= 1 && *state->mangled_cur == c) {
+// Returns true and advances "mangled_cur" if we find "one_char_token"
+// at "mangled_cur" position. It is assumed that "one_char_token" does
+// not contain '\0'.
+static bool ParseOneCharToken(State *state, const char one_char_token) {
+ if (state->mangled_cur[0] == one_char_token) {
++state->mangled_cur;
return true;
}
return false;
}
-// Returns true and advances "mangled_cur" if we find "two_chars" at
-// "mangled_cur" position.
-static bool ParseTwoChar(State *state, const char *two_chars) {
- if (RemainingLength(state) >= 2 &&
- state->mangled_cur[0] == two_chars[0] &&
- state->mangled_cur[1] == two_chars[1]) {
+// Returns true and advances "mangled_cur" if we find "two_char_token"
+// at "mangled_cur" position. It is assumed that "two_char_token" does
+// not contain '\0'.
+static bool ParseTwoCharToken(State *state, const char *two_char_token) {
+ if (state->mangled_cur[0] == two_char_token[0] &&
+ state->mangled_cur[1] == two_char_token[1]) {
state->mangled_cur += 2;
return true;
}
@@ -216,13 +223,10 @@ static bool ParseTwoChar(State *state, const char *two_chars) {
// Returns true and advances "mangled_cur" if we find any character in
// "char_class" at "mangled_cur" position.
static bool ParseCharClass(State *state, const char *char_class) {
- if (state->mangled_cur == state->mangled_end) {
- return false;
- }
const char *p = char_class;
for (; *p != '\0'; ++p) {
- if (*state->mangled_cur == *p) {
- state->mangled_cur += 1;
+ if (state->mangled_cur[0] == *p) {
+ ++state->mangled_cur;
return true;
}
}
@@ -230,7 +234,7 @@ static bool ParseCharClass(State *state, const char *char_class) {
}
// This function is used for handling an optional non-terminal.
-static bool Optional(bool status) {
+static bool Optional(bool) {
return true;
}
@@ -245,6 +249,16 @@ static bool OneOrMore(ParseFunc parse_func, State *state) {
return false;
}
+// This function is used for handling <non-terminal>* syntax. The function
+// always returns true and must be followed by a termination token or a
+// terminating sequence not handled by parse_func (e.g.
+// ParseOneCharToken(state, 'E')).
+static bool ZeroOrMore(ParseFunc parse_func, State *state) {
+ while (parse_func(state)) {
+ }
+ return true;
+}
+
// Append "str" at "out_cur". If there is an overflow, "overflowed"
// is set to true for later use. The output string is ensured to
// always terminate with '\0' as long as there is no overflow.
@@ -270,7 +284,37 @@ static bool IsLower(char c) {
}
static bool IsAlpha(char c) {
- return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+static bool IsDigit(char c) {
+ return c >= '0' && c <= '9';
+}
+
+// Returns true if "str" is a function clone suffix. These suffixes are used
+// by GCC 4.5.x and later versions to indicate functions which have been
+// cloned during optimization. We treat any sequence (.<alpha>+.<digit>+)+ as
+// a function clone suffix.
+static bool IsFunctionCloneSuffix(const char *str) {
+ size_t i = 0;
+ while (str[i] != '\0') {
+ // Consume a single .<alpha>+.<digit>+ sequence.
+ if (str[i] != '.' || !IsAlpha(str[i + 1])) {
+ return false;
+ }
+ i += 2;
+ while (IsAlpha(str[i])) {
+ ++i;
+ }
+ if (str[i] != '.' || !IsDigit(str[i + 1])) {
+ return false;
+ }
+ i += 2;
+ while (IsDigit(str[i])) {
+ ++i;
+ }
+ }
+ return true; // Consumed everything in "str".
}
// Append "str" with some tweaks, iff "append" state is true.
@@ -309,7 +353,7 @@ static bool EnterNestedName(State *state) {
}
// This function is used for handling nested names.
-static bool LeaveNestedName(State *state, int prev_value) {
+static bool LeaveNestedName(State *state, short prev_value) {
state->nest_level = prev_value;
return true;
}
@@ -349,11 +393,11 @@ static void MaybeCancelLastSeparator(State *state) {
}
}
-// Returns true if identifier pointed by "mangled_cur" is anonymous
-// namespace.
-static bool IdentifierIsAnonymousNamespace(State *state) {
- const char anon_prefix[] = "_GLOBAL__N_";
- return (state->number > sizeof(anon_prefix) - 1 && // Should be longer.
+// Returns true if the identifier of the given length pointed to by
+// "mangled_cur" is anonymous namespace.
+static bool IdentifierIsAnonymousNamespace(State *state, int length) {
+ static const char anon_prefix[] = "_GLOBAL__N_";
+ return (length > (int)sizeof(anon_prefix) - 1 && // Should be longer.
StrPrefix(state->mangled_cur, anon_prefix));
}
@@ -368,10 +412,10 @@ static bool ParsePrefix(State *state);
static bool ParseUnqualifiedName(State *state);
static bool ParseSourceName(State *state);
static bool ParseLocalSourceName(State *state);
-static bool ParseNumber(State *state);
+static bool ParseNumber(State *state, int *number_out);
static bool ParseFloatNumber(State *state);
static bool ParseSeqId(State *state);
-static bool ParseIdentifier(State *state);
+static bool ParseIdentifier(State *state, int length);
static bool ParseOperatorName(State *state);
static bool ParseSpecialName(State *state);
static bool ParseCallOffset(State *state);
@@ -428,17 +472,7 @@ static bool ParseSubstitution(State *state);
// <mangled-name> ::= _Z <encoding>
static bool ParseMangledName(State *state) {
- if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) {
- // Append trailing version suffix if any.
- // ex. _Z3foo@@GLIBCXX_3.4
- if (state->mangled_cur < state->mangled_end &&
- state->mangled_cur[0] == '@') {
- MaybeAppend(state, state->mangled_cur);
- state->mangled_cur = state->mangled_end;
- }
- return true;
- }
- return false;
+ return ParseTwoCharToken(state, "_Z") && ParseEncoding(state);
}
// <encoding> ::= <(function) name> <bare-function-type>
@@ -488,7 +522,7 @@ static bool ParseUnscopedName(State *state) {
}
State copy = *state;
- if (ParseTwoChar(state, "St") &&
+ if (ParseTwoCharToken(state, "St") &&
MaybeAppend(state, "std::") &&
ParseUnqualifiedName(state)) {
return true;
@@ -507,12 +541,12 @@ static bool ParseUnscopedTemplateName(State *state) {
// ::= N [<CV-qualifiers>] <template-prefix> <template-args> E
static bool ParseNestedName(State *state) {
State copy = *state;
- if (ParseChar(state, 'N') &&
+ if (ParseOneCharToken(state, 'N') &&
EnterNestedName(state) &&
Optional(ParseCVQualifiers(state)) &&
ParsePrefix(state) &&
LeaveNestedName(state, copy.nest_level) &&
- ParseChar(state, 'E')) {
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
@@ -565,7 +599,8 @@ static bool ParseUnqualifiedName(State *state) {
// <source-name> ::= <positive length number> <identifier>
static bool ParseSourceName(State *state) {
State copy = *state;
- if (ParseNumber(state) && ParseIdentifier(state)) {
+ int length = -1;
+ if (ParseNumber(state, &length) && ParseIdentifier(state, length)) {
return true;
}
*state = copy;
@@ -579,7 +614,7 @@ static bool ParseSourceName(State *state) {
// http://gcc.gnu.org/viewcvs?view=rev&revision=124467
static bool ParseLocalSourceName(State *state) {
State copy = *state;
- if (ParseChar(state, 'L') && ParseSourceName(state) &&
+ if (ParseOneCharToken(state, 'L') && ParseSourceName(state) &&
Optional(ParseDiscriminator(state))) {
return true;
}
@@ -588,15 +623,17 @@ static bool ParseLocalSourceName(State *state) {
}
// <number> ::= [n] <non-negative decimal integer>
-static bool ParseNumber(State *state) {
+// If "number_out" is non-null, then *number_out is set to the value of the
+// parsed number on success.
+static bool ParseNumber(State *state, int *number_out) {
int sign = 1;
- if (ParseChar(state, 'n')) {
+ if (ParseOneCharToken(state, 'n')) {
sign = -1;
}
const char *p = state->mangled_cur;
int number = 0;
- for (;p < state->mangled_end; ++p) {
- if ((*p >= '0' && *p <= '9')) {
+ for (;*p != '\0'; ++p) {
+ if (IsDigit(*p)) {
number = number * 10 + (*p - '0');
} else {
break;
@@ -604,7 +641,9 @@ static bool ParseNumber(State *state) {
}
if (p != state->mangled_cur) { // Conversion succeeded.
state->mangled_cur = p;
- state->number = number * sign;
+ if (number_out != NULL) {
+ *number_out = number * sign;
+ }
return true;
}
return false;
@@ -614,19 +653,13 @@ static bool ParseNumber(State *state) {
// hexadecimal string.
static bool ParseFloatNumber(State *state) {
const char *p = state->mangled_cur;
- int number = 0;
- for (;p < state->mangled_end; ++p) {
- if ((*p >= '0' && *p <= '9')) {
- number = number * 16 + (*p - '0');
- } else if (*p >= 'a' && *p <= 'f') {
- number = number * 16 + (*p - 'a' + 10);
- } else {
+ for (;*p != '\0'; ++p) {
+ if (!IsDigit(*p) && !(*p >= 'a' && *p <= 'f')) {
break;
}
}
if (p != state->mangled_cur) { // Conversion succeeded.
state->mangled_cur = p;
- state->number = number;
return true;
}
return false;
@@ -636,37 +669,30 @@ static bool ParseFloatNumber(State *state) {
// using digits and upper case letters
static bool ParseSeqId(State *state) {
const char *p = state->mangled_cur;
- int number = 0;
- for (;p < state->mangled_end; ++p) {
- if ((*p >= '0' && *p <= '9')) {
- number = number * 36 + (*p - '0');
- } else if (*p >= 'A' && *p <= 'Z') {
- number = number * 36 + (*p - 'A' + 10);
- } else {
+ for (;*p != '\0'; ++p) {
+ if (!IsDigit(*p) && !(*p >= 'A' && *p <= 'Z')) {
break;
}
}
if (p != state->mangled_cur) { // Conversion succeeded.
state->mangled_cur = p;
- state->number = number;
return true;
}
return false;
}
-// <identifier> ::= <unqualified source code identifier>
-static bool ParseIdentifier(State *state) {
- if (state->number == -1 ||
- RemainingLength(state) < state->number) {
+// <identifier> ::= <unqualified source code identifier> (of given length)
+static bool ParseIdentifier(State *state, int length) {
+ if (length == -1 ||
+ !AtLeastNumCharsRemaining(state->mangled_cur, length)) {
return false;
}
- if (IdentifierIsAnonymousNamespace(state)) {
+ if (IdentifierIsAnonymousNamespace(state, length)) {
MaybeAppend(state, "(anonymous namespace)");
} else {
- MaybeAppendWithLength(state, state->mangled_cur, state->number);
+ MaybeAppendWithLength(state, state->mangled_cur, length);
}
- state->mangled_cur += state->number;
- state->number = -1; // Reset the number.
+ state->mangled_cur += length;
return true;
}
@@ -674,12 +700,12 @@ static bool ParseIdentifier(State *state) {
// ::= cv <type> # (cast)
// ::= v <digit> <source-name> # vendor extended operator
static bool ParseOperatorName(State *state) {
- if (RemainingLength(state) < 2) {
+ if (!AtLeastNumCharsRemaining(state->mangled_cur, 2)) {
return false;
}
// First check with "cv" (cast) case.
State copy = *state;
- if (ParseTwoChar(state, "cv") &&
+ if (ParseTwoCharToken(state, "cv") &&
MaybeAppend(state, "operator ") &&
EnterNestedName(state) &&
ParseType(state) &&
@@ -689,7 +715,7 @@ static bool ParseOperatorName(State *state) {
*state = copy;
// Then vendor extended operators.
- if (ParseChar(state, 'v') && ParseCharClass(state, "0123456789") &&
+ if (ParseOneCharToken(state, 'v') && ParseCharClass(state, "0123456789") &&
ParseSourceName(state)) {
return true;
}
@@ -738,34 +764,34 @@ static bool ParseOperatorName(State *state) {
// stack traces. The are special data.
static bool ParseSpecialName(State *state) {
State copy = *state;
- if (ParseChar(state, 'T') &&
+ if (ParseOneCharToken(state, 'T') &&
ParseCharClass(state, "VTIS") &&
ParseType(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "Tc") && ParseCallOffset(state) &&
+ if (ParseTwoCharToken(state, "Tc") && ParseCallOffset(state) &&
ParseCallOffset(state) && ParseEncoding(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "GV") &&
+ if (ParseTwoCharToken(state, "GV") &&
ParseName(state)) {
return true;
}
*state = copy;
- if (ParseChar(state, 'T') && ParseCallOffset(state) &&
+ if (ParseOneCharToken(state, 'T') && ParseCallOffset(state) &&
ParseEncoding(state)) {
return true;
}
*state = copy;
// G++ extensions
- if (ParseTwoChar(state, "TC") && ParseType(state) &&
- ParseNumber(state) && ParseChar(state, '_') &&
+ if (ParseTwoCharToken(state, "TC") && ParseType(state) &&
+ ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
DisableAppend(state) &&
ParseType(state)) {
RestoreAppend(state, copy.append);
@@ -773,23 +799,23 @@ static bool ParseSpecialName(State *state) {
}
*state = copy;
- if (ParseChar(state, 'T') && ParseCharClass(state, "FJ") &&
+ if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "FJ") &&
ParseType(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "GR") && ParseName(state)) {
+ if (ParseTwoCharToken(state, "GR") && ParseName(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "GA") && ParseEncoding(state)) {
+ if (ParseTwoCharToken(state, "GA") && ParseEncoding(state)) {
return true;
}
*state = copy;
- if (ParseChar(state, 'T') && ParseCharClass(state, "hv") &&
+ if (ParseOneCharToken(state, 'T') && ParseCharClass(state, "hv") &&
ParseCallOffset(state) && ParseEncoding(state)) {
return true;
}
@@ -801,14 +827,14 @@ static bool ParseSpecialName(State *state) {
// ::= v <v-offset> _
static bool ParseCallOffset(State *state) {
State copy = *state;
- if (ParseChar(state, 'h') &&
- ParseNVOffset(state) && ParseChar(state, '_')) {
+ if (ParseOneCharToken(state, 'h') &&
+ ParseNVOffset(state) && ParseOneCharToken(state, '_')) {
return true;
}
*state = copy;
- if (ParseChar(state, 'v') &&
- ParseVOffset(state) && ParseChar(state, '_')) {
+ if (ParseOneCharToken(state, 'v') &&
+ ParseVOffset(state) && ParseOneCharToken(state, '_')) {
return true;
}
*state = copy;
@@ -818,14 +844,14 @@ static bool ParseCallOffset(State *state) {
// <nv-offset> ::= <(offset) number>
static bool ParseNVOffset(State *state) {
- return ParseNumber(state);
+ return ParseNumber(state, NULL);
}
// <v-offset> ::= <(offset) number> _ <(virtual offset) number>
static bool ParseVOffset(State *state) {
State copy = *state;
- if (ParseNumber(state) && ParseChar(state, '_') &&
- ParseNumber(state)) {
+ if (ParseNumber(state, NULL) && ParseOneCharToken(state, '_') &&
+ ParseNumber(state, NULL)) {
return true;
}
*state = copy;
@@ -836,7 +862,7 @@ static bool ParseVOffset(State *state) {
// ::= D0 | D1 | D2
static bool ParseCtorDtorName(State *state) {
State copy = *state;
- if (ParseChar(state, 'C') &&
+ if (ParseOneCharToken(state, 'C') &&
ParseCharClass(state, "123")) {
const char * const prev_name = state->prev_name;
const int prev_name_length = state->prev_name_length;
@@ -845,7 +871,7 @@ static bool ParseCtorDtorName(State *state) {
}
*state = copy;
- if (ParseChar(state, 'D') &&
+ if (ParseOneCharToken(state, 'D') &&
ParseCharClass(state, "012")) {
const char * const prev_name = state->prev_name;
const int prev_name_length = state->prev_name_length;
@@ -858,11 +884,12 @@ static bool ParseCtorDtorName(State *state) {
}
// <type> ::= <CV-qualifiers> <type>
-// ::= P <type>
-// ::= R <type>
-// ::= C <type>
-// ::= G <type>
-// ::= U <source-name> <type>
+// ::= P <type> # pointer-to
+// ::= R <type> # reference-to
+// ::= O <type> # rvalue reference-to (C++0x)
+// ::= C <type> # complex pair (C 2000)
+// ::= G <type> # imaginary (C 2000)
+// ::= U <source-name> <type> # vendor extended type qualifier
// ::= <builtin-type>
// ::= <function-type>
// ::= <class-enum-type>
@@ -871,6 +898,11 @@ static bool ParseCtorDtorName(State *state) {
// ::= <template-template-param> <template-args>
// ::= <template-param>
// ::= <substitution>
+// ::= Dp <type> # pack expansion of (C++0x)
+// ::= Dt <expression> E # decltype of an id-expression or class
+// # member access (C++0x)
+// ::= DT <expression> E # decltype of an expression (C++0x)
+//
static bool ParseType(State *state) {
// We should check CV-qualifers, and PRGC things first.
State copy = *state;
@@ -879,12 +911,23 @@ static bool ParseType(State *state) {
}
*state = copy;
- if (ParseCharClass(state, "PRCG") && ParseType(state)) {
+ if (ParseCharClass(state, "OPRCG") && ParseType(state)) {
return true;
}
*state = copy;
- if (ParseChar(state, 'U') && ParseSourceName(state) &&
+ if (ParseTwoCharToken(state, "Dp") && ParseType(state)) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseOneCharToken(state, 'D') && ParseCharClass(state, "tT") &&
+ ParseExpression(state) && ParseOneCharToken(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
+ if (ParseOneCharToken(state, 'U') && ParseSourceName(state) &&
ParseType(state)) {
return true;
}
@@ -918,9 +961,9 @@ static bool ParseType(State *state) {
// ParseType().
static bool ParseCVQualifiers(State *state) {
int num_cv_qualifiers = 0;
- num_cv_qualifiers += ParseChar(state, 'r');
- num_cv_qualifiers += ParseChar(state, 'V');
- num_cv_qualifiers += ParseChar(state, 'K');
+ num_cv_qualifiers += ParseOneCharToken(state, 'r');
+ num_cv_qualifiers += ParseOneCharToken(state, 'V');
+ num_cv_qualifiers += ParseOneCharToken(state, 'K');
return num_cv_qualifiers > 0;
}
@@ -937,7 +980,7 @@ static bool ParseBuiltinType(State *state) {
}
State copy = *state;
- if (ParseChar(state, 'u') && ParseSourceName(state)) {
+ if (ParseOneCharToken(state, 'u') && ParseSourceName(state)) {
return true;
}
*state = copy;
@@ -947,8 +990,9 @@ static bool ParseBuiltinType(State *state) {
// <function-type> ::= F [Y] <bare-function-type> E
static bool ParseFunctionType(State *state) {
State copy = *state;
- if (ParseChar(state, 'F') && Optional(ParseChar(state, 'Y')) &&
- ParseBareFunctionType(state) && ParseChar(state, 'E')) {
+ if (ParseOneCharToken(state, 'F') &&
+ Optional(ParseOneCharToken(state, 'Y')) &&
+ ParseBareFunctionType(state) && ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
@@ -977,14 +1021,14 @@ static bool ParseClassEnumType(State *state) {
// ::= A [<(dimension) expression>] _ <(element) type>
static bool ParseArrayType(State *state) {
State copy = *state;
- if (ParseChar(state, 'A') && ParseNumber(state) &&
- ParseChar(state, '_') && ParseType(state)) {
+ if (ParseOneCharToken(state, 'A') && ParseNumber(state, NULL) &&
+ ParseOneCharToken(state, '_') && ParseType(state)) {
return true;
}
*state = copy;
- if (ParseChar(state, 'A') && Optional(ParseExpression(state)) &&
- ParseChar(state, '_') && ParseType(state)) {
+ if (ParseOneCharToken(state, 'A') && Optional(ParseExpression(state)) &&
+ ParseOneCharToken(state, '_') && ParseType(state)) {
return true;
}
*state = copy;
@@ -994,7 +1038,7 @@ static bool ParseArrayType(State *state) {
// <pointer-to-member-type> ::= M <(class) type> <(member) type>
static bool ParsePointerToMemberType(State *state) {
State copy = *state;
- if (ParseChar(state, 'M') && ParseType(state) &&
+ if (ParseOneCharToken(state, 'M') && ParseType(state) &&
ParseType(state)) {
return true;
}
@@ -1005,14 +1049,14 @@ static bool ParsePointerToMemberType(State *state) {
// <template-param> ::= T_
// ::= T <parameter-2 non-negative number> _
static bool ParseTemplateParam(State *state) {
- if (ParseTwoChar(state, "T_")) {
+ if (ParseTwoCharToken(state, "T_")) {
MaybeAppend(state, "?"); // We don't support template substitutions.
return true;
}
State copy = *state;
- if (ParseChar(state, 'T') && ParseNumber(state) &&
- ParseChar(state, '_')) {
+ if (ParseOneCharToken(state, 'T') && ParseNumber(state, NULL) &&
+ ParseOneCharToken(state, '_')) {
MaybeAppend(state, "?"); // We don't support template substitutions.
return true;
}
@@ -1032,9 +1076,9 @@ static bool ParseTemplateTemplateParam(State *state) {
static bool ParseTemplateArgs(State *state) {
State copy = *state;
DisableAppend(state);
- if (ParseChar(state, 'I') &&
+ if (ParseOneCharToken(state, 'I') &&
OneOrMore(ParseTemplateArg, state) &&
- ParseChar(state, 'E')) {
+ ParseOneCharToken(state, 'E')) {
RestoreAppend(state, copy.append);
MaybeAppend(state, "<>");
return true;
@@ -1045,16 +1089,25 @@ static bool ParseTemplateArgs(State *state) {
// <template-arg> ::= <type>
// ::= <expr-primary>
+// ::= I <template-arg>* E # argument pack
// ::= X <expression> E
static bool ParseTemplateArg(State *state) {
+ State copy = *state;
+ if (ParseOneCharToken(state, 'I') &&
+ ZeroOrMore(ParseTemplateArg, state) &&
+ ParseOneCharToken(state, 'E')) {
+ return true;
+ }
+ *state = copy;
+
if (ParseType(state) ||
ParseExprPrimary(state)) {
return true;
}
+ *state = copy;
- State copy = *state;
- if (ParseChar(state, 'X') && ParseExpression(state) &&
- ParseChar(state, 'E')) {
+ if (ParseOneCharToken(state, 'X') && ParseExpression(state) &&
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
@@ -1097,19 +1150,19 @@ static bool ParseExpression(State *state) {
}
*state = copy;
- if (ParseTwoChar(state, "st") && ParseType(state)) {
+ if (ParseTwoCharToken(state, "st") && ParseType(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "sr") && ParseType(state) &&
+ if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
ParseUnqualifiedName(state) &&
ParseTemplateArgs(state)) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "sr") && ParseType(state) &&
+ if (ParseTwoCharToken(state, "sr") && ParseType(state) &&
ParseUnqualifiedName(state)) {
return true;
}
@@ -1124,28 +1177,28 @@ static bool ParseExpression(State *state) {
// ::= LZ <encoding> E
static bool ParseExprPrimary(State *state) {
State copy = *state;
- if (ParseChar(state, 'L') && ParseType(state) &&
- ParseNumber(state) &&
- ParseChar(state, 'E')) {
+ if (ParseOneCharToken(state, 'L') && ParseType(state) &&
+ ParseNumber(state, NULL) &&
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
- if (ParseChar(state, 'L') && ParseType(state) &&
+ if (ParseOneCharToken(state, 'L') && ParseType(state) &&
ParseFloatNumber(state) &&
- ParseChar(state, 'E')) {
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
- if (ParseChar(state, 'L') && ParseMangledName(state) &&
- ParseChar(state, 'E')) {
+ if (ParseOneCharToken(state, 'L') && ParseMangledName(state) &&
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
- if (ParseTwoChar(state, "LZ") && ParseEncoding(state) &&
- ParseChar(state, 'E')) {
+ if (ParseTwoCharToken(state, "LZ") && ParseEncoding(state) &&
+ ParseOneCharToken(state, 'E')) {
return true;
}
*state = copy;
@@ -1158,15 +1211,15 @@ static bool ParseExprPrimary(State *state) {
// := Z <(function) encoding> E s [<discriminator>]
static bool ParseLocalName(State *state) {
State copy = *state;
- if (ParseChar(state, 'Z') && ParseEncoding(state) &&
- ParseChar(state, 'E') && MaybeAppend(state, "::") &&
+ if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
+ ParseOneCharToken(state, 'E') && MaybeAppend(state, "::") &&
ParseName(state) && Optional(ParseDiscriminator(state))) {
return true;
}
*state = copy;
- if (ParseChar(state, 'Z') && ParseEncoding(state) &&
- ParseTwoChar(state, "Es") && Optional(ParseDiscriminator(state))) {
+ if (ParseOneCharToken(state, 'Z') && ParseEncoding(state) &&
+ ParseTwoCharToken(state, "Es") && Optional(ParseDiscriminator(state))) {
return true;
}
*state = copy;
@@ -1176,7 +1229,7 @@ static bool ParseLocalName(State *state) {
// <discriminator> := _ <(non-negative) number>
static bool ParseDiscriminator(State *state) {
State copy = *state;
- if (ParseChar(state, '_') && ParseNumber(state)) {
+ if (ParseOneCharToken(state, '_') && ParseNumber(state, NULL)) {
return true;
}
*state = copy;
@@ -1187,21 +1240,21 @@ static bool ParseDiscriminator(State *state) {
// ::= S <seq-id> _
// ::= St, etc.
static bool ParseSubstitution(State *state) {
- if (ParseTwoChar(state, "S_")) {
+ if (ParseTwoCharToken(state, "S_")) {
MaybeAppend(state, "?"); // We don't support substitutions.
return true;
}
State copy = *state;
- if (ParseChar(state, 'S') && ParseSeqId(state) &&
- ParseChar(state, '_')) {
+ if (ParseOneCharToken(state, 'S') && ParseSeqId(state) &&
+ ParseOneCharToken(state, '_')) {
MaybeAppend(state, "?"); // We don't support substitutions.
return true;
}
*state = copy;
// Expand abbreviations like "St" => "std".
- if (ParseChar(state, 'S')) {
+ if (ParseOneCharToken(state, 'S')) {
const AbbrevPair *p;
for (p = kSubstitutionList; p->abbrev != NULL; ++p) {
if (state->mangled_cur[0] == p->abbrev[1]) {
@@ -1210,7 +1263,7 @@ static bool ParseSubstitution(State *state) {
MaybeAppend(state, "::");
MaybeAppend(state, p->real_name);
}
- state->mangled_cur += 1;
+ ++state->mangled_cur;
return true;
}
}
@@ -1219,13 +1272,33 @@ static bool ParseSubstitution(State *state) {
return false;
}
+// Parse <mangled-name>, optionally followed by either a function-clone suffix
+// or version suffix. Returns true only if all of "mangled_cur" was consumed.
+static bool ParseTopLevelMangledName(State *state) {
+ if (ParseMangledName(state)) {
+ if (state->mangled_cur[0] != '\0') {
+ // Drop trailing function clone suffix, if any.
+ if (IsFunctionCloneSuffix(state->mangled_cur)) {
+ return true;
+ }
+ // Append trailing version suffix if any.
+ // ex. _Z3foo@@GLIBCXX_3.4
+ if (state->mangled_cur[0] == '@') {
+ MaybeAppend(state, state->mangled_cur);
+ return true;
+ }
+ return false; // Unconsumed suffix.
+ }
+ return true;
+ }
+ return false;
+}
+
// The demangler entry point.
bool Demangle(const char *mangled, char *out, int out_size) {
State state;
InitState(&state, mangled, out, out_size);
- return (ParseMangledName(&state) &&
- state.overflowed == false &&
- RemainingLength(&state) == 0);
+ return ParseTopLevelMangledName(&state) && !state.overflowed;
}
_END_GOOGLE_NAMESPACE_
diff --git a/chromium/base/third_party/symbolize/symbolize.cc b/chromium/base/third_party/symbolize/symbolize.cc
index 85976bbc7b9..b25f7479d0d 100644
--- a/chromium/base/third_party/symbolize/symbolize.cc
+++ b/chromium/base/third_party/symbolize/symbolize.cc
@@ -45,8 +45,13 @@
// some functions which are not guaranteed to be so, such as memchr()
// and memmove(). We assume they are async-signal-safe.
//
+// Additional header can be specified by the GLOG_BUILD_CONFIG_INCLUDE
+// macro to add platform specific defines (e.g. OS_OPENBSD).
+
+#ifdef GLOG_BUILD_CONFIG_INCLUDE
+#include GLOG_BUILD_CONFIG_INCLUDE
+#endif // GLOG_BUILD_CONFIG_INCLUDE
-#include "build/build_config.h"
#include "utilities.h"
#if defined(HAVE_SYMBOLIZE)
@@ -75,6 +80,13 @@ void InstallSymbolizeCallback(SymbolizeCallback callback) {
g_symbolize_callback = callback;
}
+static SymbolizeOpenObjectFileCallback g_symbolize_open_object_file_callback =
+ NULL;
+void InstallSymbolizeOpenObjectFileCallback(
+ SymbolizeOpenObjectFileCallback callback) {
+ g_symbolize_open_object_file_callback = callback;
+}
+
// This function wraps the Demangle function to provide an interface
// where the input symbol is demangled in-place.
// To keep stack consumption low, we would like this function to not
@@ -83,8 +95,8 @@ static ATTRIBUTE_NOINLINE void DemangleInplace(char *out, int out_size) {
char demangled[256]; // Big enough for sane demangled symbols.
if (Demangle(out, demangled, sizeof(demangled))) {
// Demangling succeeded. Copy to out if the space allows.
- int len = strlen(demangled);
- if (len + 1 <= out_size) { // +1 for '\0'.
+ size_t len = strlen(demangled);
+ if (len + 1 <= (size_t)out_size) { // +1 for '\0'.
SAFE_ASSERT(len < sizeof(demangled));
memmove(out, demangled, len + 1);
}
@@ -483,13 +495,20 @@ static char *GetHex(const char *start, const char *end, uint64_t *hex) {
return const_cast<char *>(p);
}
-// Search for the object file (from /proc/self/maps) that contains
-// the specified pc. If found, open this file and return the file handle,
-// and also set start_address to the start address of where this object
-// file is mapped to in memory. Otherwise, return -1.
+// Searches for the object file (from /proc/self/maps) that contains
+// the specified pc. If found, sets |start_address| to the start address
+// of where this object file is mapped in memory, sets the module base
+// address into |base_address|, copies the object file name into
+// |out_file_name|, and attempts to open the object file. If the object
+// file is opened successfully, returns the file descriptor. Otherwise,
+// returns -1. |out_file_name_size| is the size of the file name buffer
+// (including the null-terminator).
static ATTRIBUTE_NOINLINE int
OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
- uint64_t &start_address) {
+ uint64_t &start_address,
+ uint64_t &base_address,
+ char *out_file_name,
+ int out_file_name_size) {
int object_fd;
// Open /proc/self/maps.
@@ -503,8 +522,10 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
// Iterate over maps and look for the map containing the pc. Then
// look into the symbol tables inside.
char buf[1024]; // Big enough for line of sane /proc/self/maps
+ int num_maps = 0;
LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf));
while (true) {
+ num_maps++;
const char *cursor;
const char *eol;
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
@@ -554,14 +575,35 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
}
++cursor; // Skip ' '.
- // Skip to file name. "cursor" now points to file offset. We need to
- // skip at least three spaces for file offset, dev, and inode.
+ // Read file offset.
+ uint64_t file_offset;
+ cursor = GetHex(cursor, eol, &file_offset);
+ if (cursor == eol || *cursor != ' ') {
+ return -1; // Malformed line.
+ }
+ ++cursor; // Skip ' '.
+
+ // Don't subtract 'start_address' from the first entry:
+ // * If a binary is compiled w/o -pie, then the first entry in
+ // process maps is likely the binary itself (all dynamic libs
+ // are mapped higher in address space). For such a binary,
+ // instruction offset in binary coincides with the actual
+ // instruction address in virtual memory (as code section
+ // is mapped to a fixed memory range).
+ // * If a binary is compiled with -pie, all the modules are
+ // mapped high at address space (in particular, higher than
+ // shadow memory of the tool), so the module can't be the
+ // first entry.
+ base_address = ((num_maps == 1) ? 0U : start_address) - file_offset;
+
+ // Skip to file name. "cursor" now points to dev. We need to
+ // skip at least two spaces for dev and inode.
int num_spaces = 0;
while (cursor < eol) {
if (*cursor == ' ') {
++num_spaces;
- } else if (num_spaces >= 3) {
- // The first non-space character after skipping three spaces
+ } else if (num_spaces >= 2) {
+ // The first non-space character after skipping two spaces
// is the beginning of the file name.
break;
}
@@ -574,12 +616,105 @@ OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
// Finally, "cursor" now points to file name of our interest.
NO_INTR(object_fd = open(cursor, O_RDONLY));
if (object_fd < 0) {
+ // Failed to open object file. Copy the object file name to
+ // |out_file_name|.
+ strncpy(out_file_name, cursor, out_file_name_size);
+ // Making sure |out_file_name| is always null-terminated.
+ out_file_name[out_file_name_size - 1] = '\0';
return -1;
}
return object_fd;
}
}
+// 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.
+// NOTE: code from sandbox/linux/seccomp-bpf/demo.cc.
+char *itoa_r(intptr_t i, char *buf, size_t sz, int base, size_t padding) {
+ // 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;
+
+ if (padding > 0)
+ padding--;
+ } while (j > 0 || padding > 0);
+
+ // 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;
+}
+
+// Safely appends string |source| to string |dest|. Never writes past the
+// buffer size |dest_size| and guarantees that |dest| is null-terminated.
+void SafeAppendString(const char* source, char* dest, int dest_size) {
+ int dest_string_length = strlen(dest);
+ SAFE_ASSERT(dest_string_length < dest_size);
+ dest += dest_string_length;
+ dest_size -= dest_string_length;
+ strncpy(dest, source, dest_size);
+ // Making sure |dest| is always null-terminated.
+ dest[dest_size - 1] = '\0';
+}
+
+// Converts a 64-bit value into a hex string, and safely appends it to |dest|.
+// Never writes past the buffer size |dest_size| and guarantees that |dest| is
+// null-terminated.
+void SafeAppendHexNumber(uint64_t value, char* dest, int dest_size) {
+ // 64-bit numbers in hex can have up to 16 digits.
+ char buf[17] = {'\0'};
+ SafeAppendString(itoa_r(value, buf, sizeof(buf), 16, 0), dest, dest_size);
+}
+
// The implementation of our symbolization routine. If it
// successfully finds the symbol containing "pc" and obtains the
// symbol name, returns true and write the symbol name to "out".
@@ -592,10 +727,40 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
int out_size) {
uint64_t pc0 = reinterpret_cast<uintptr_t>(pc);
uint64_t start_address = 0;
+ uint64_t base_address = 0;
+ int object_fd = -1;
- int object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0,
- start_address);
- if (object_fd == -1) {
+ if (out_size < 1) {
+ return false;
+ }
+ out[0] = '\0';
+ SafeAppendString("(", out, out_size);
+
+ if (g_symbolize_open_object_file_callback) {
+ object_fd = g_symbolize_open_object_file_callback(pc0, start_address,
+ base_address, out + 1,
+ out_size - 1);
+ } else {
+ object_fd = OpenObjectFileContainingPcAndGetStartAddress(pc0, start_address,
+ base_address,
+ out + 1,
+ out_size - 1);
+ }
+
+ // Check whether a file name was returned.
+ if (object_fd < 0) {
+ if (out[1]) {
+ // The object file containing PC was determined successfully however the
+ // object file was not opened successfully. This is still considered
+ // success because the object file name and offset are known and tools
+ // like asan_symbolize.py can be used for the symbolization.
+ out[out_size - 1] = '\0'; // Making sure |out| is always null-terminated.
+ SafeAppendString("+0x", out, out_size);
+ SafeAppendHexNumber(pc0 - base_address, out, out_size);
+ SafeAppendString(")", out, out_size);
+ return true;
+ }
+ // Failed to determine the object file containing PC. Bail out.
return false;
}
FileDescriptor wrapped_object_fd(object_fd);
@@ -639,7 +804,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void *pc, char *out,
int out_size) {
Dl_info info;
if (dladdr(pc, &info)) {
- if (strlen(info.dli_sname) < out_size) {
+ if ((int)strlen(info.dli_sname) < out_size) {
strcpy(out, info.dli_sname);
// Symbolization succeeded. Now we try to demangle the symbol.
DemangleInplace(out, out_size);
diff --git a/chromium/base/third_party/symbolize/symbolize.h b/chromium/base/third_party/symbolize/symbolize.h
index 441e5436962..f617184249c 100644
--- a/chromium/base/third_party/symbolize/symbolize.h
+++ b/chromium/base/third_party/symbolize/symbolize.h
@@ -105,6 +105,10 @@ _END_GOOGLE_NAMESPACE_
_START_GOOGLE_NAMESPACE_
+// Restrictions on the callbacks that follow:
+// - The callbacks must not use heaps but only use stacks.
+// - The callbacks must be async-signal-safe.
+
// Installs a callback function, which will be called right before a symbol name
// is printed. The callback is intended to be used for showing a file name and a
// line number preceding a symbol name.
@@ -116,6 +120,24 @@ typedef int (*SymbolizeCallback)(int fd, void *pc, char *out, size_t out_size,
uint64 relocation);
void InstallSymbolizeCallback(SymbolizeCallback callback);
+// Installs a callback function, which will be called instead of
+// OpenObjectFileContainingPcAndGetStartAddress. The callback is expected
+// to searches for the object file (from /proc/self/maps) that contains
+// the specified pc. If found, sets |start_address| to the start address
+// of where this object file is mapped in memory, sets the module base
+// address into |base_address|, copies the object file name into
+// |out_file_name|, and attempts to open the object file. If the object
+// file is opened successfully, returns the file descriptor. Otherwise,
+// returns -1. |out_file_name_size| is the size of the file name buffer
+// (including the null-terminator).
+typedef int (*SymbolizeOpenObjectFileCallback)(uint64_t pc,
+ uint64_t &start_address,
+ uint64_t &base_address,
+ char *out_file_name,
+ int out_file_name_size);
+void InstallSymbolizeOpenObjectFileCallback(
+ SymbolizeOpenObjectFileCallback callback);
+
_END_GOOGLE_NAMESPACE_
#endif
diff --git a/chromium/base/third_party/xdg_mime/BUILD.gn b/chromium/base/third_party/xdg_mime/BUILD.gn
new file mode 100644
index 00000000000..3e51e33a1d2
--- /dev/null
+++ b/chromium/base/third_party/xdg_mime/BUILD.gn
@@ -0,0 +1,28 @@
+# 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.
+
+source_set("xdg_mime") {
+ visibility = "//base/*"
+ sources = [
+ "xdgmime.c",
+ "xdgmime.h",
+ "xdgmimealias.c",
+ "xdgmimealias.h",
+ "xdgmimecache.c",
+ "xdgmimecache.h",
+ "xdgmimeglob.c",
+ "xdgmimeglob.h",
+ "xdgmimeicon.c",
+ "xdgmimeicon.h",
+ "xdgmimeint.c",
+ "xdgmimeint.h",
+ "xdgmimemagic.c",
+ "xdgmimemagic.h",
+ "xdgmimeparent.c",
+ "xdgmimeparent.h",
+ ]
+
+ configs -= [ "//build/config/compiler:chromium_code" ]
+ configs += [ "//build/config/compiler:no_chromium_code" ]
+}
diff --git a/chromium/base/third_party/xdg_user_dirs/BUILD.gn b/chromium/base/third_party/xdg_user_dirs/BUILD.gn
new file mode 100644
index 00000000000..d866960bf1d
--- /dev/null
+++ b/chromium/base/third_party/xdg_user_dirs/BUILD.gn
@@ -0,0 +1,11 @@
+# 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.
+
+source_set("xdg_user_dirs") {
+ visibility = "//base/*"
+ sources = [
+ "xdg_user_dir_lookup.cc",
+ "xdg_user_dir_lookup.h",
+ ]
+}
diff --git a/chromium/base/threading/non_thread_safe.h b/chromium/base/threading/non_thread_safe.h
index a27bb0ea0c5..d41c08608c9 100644
--- a/chromium/base/threading/non_thread_safe.h
+++ b/chromium/base/threading/non_thread_safe.h
@@ -18,9 +18,7 @@
#define ENABLE_NON_THREAD_SAFE 0
#endif
-#if ENABLE_NON_THREAD_SAFE
#include "base/threading/non_thread_safe_impl.h"
-#endif
namespace base {
@@ -70,4 +68,4 @@ typedef NonThreadSafeDoNothing NonThreadSafe;
} // namespace base
-#endif // BASE_NON_THREAD_SAFE_H_
+#endif // BASE_THREADING_NON_THREAD_SAFE_H_
diff --git a/chromium/base/threading/platform_thread.h b/chromium/base/threading/platform_thread.h
index 9742d570c6f..28743145610 100644
--- a/chromium/base/threading/platform_thread.h
+++ b/chromium/base/threading/platform_thread.h
@@ -23,12 +23,48 @@
namespace base {
+// Used for logging. Always an integer value.
#if defined(OS_WIN)
typedef DWORD PlatformThreadId;
#elif defined(OS_POSIX)
typedef pid_t PlatformThreadId;
#endif
+// Used for thread checking and debugging.
+// Meant to be as fast as possible.
+// These are produced by PlatformThread::CurrentRef(), and used to later
+// check if we are on the same thread or not by using ==. These are safe
+// to copy between threads, but can't be copied to another process as they
+// have no meaning there. Also, the internal identifier can be re-used
+// after a thread dies, so a PlatformThreadRef cannot be reliably used
+// to distinguish a new thread from an old, dead thread.
+class PlatformThreadRef {
+ public:
+#if defined(OS_WIN)
+ typedef DWORD RefType;
+#elif defined(OS_POSIX)
+ typedef pthread_t RefType;
+#endif
+ PlatformThreadRef()
+ : id_(0) {
+ }
+
+ explicit PlatformThreadRef(RefType id)
+ : id_(id) {
+ }
+
+ bool operator==(PlatformThreadRef other) const {
+ return id_ == other.id_;
+ }
+
+ bool is_null() const {
+ return id_ == 0;
+ }
+ private:
+ RefType id_;
+};
+
+// Used to operate on threads.
class PlatformThreadHandle {
public:
#if defined(OS_WIN)
@@ -53,15 +89,15 @@ class PlatformThreadHandle {
id_(id) {
}
- bool is_equal(const PlatformThreadHandle& other) {
+ bool is_equal(const PlatformThreadHandle& other) const {
return handle_ == other.handle_;
}
- bool is_null() {
+ bool is_null() const {
return !handle_;
}
- Handle platform_handle() {
+ Handle platform_handle() const {
return handle_;
}
@@ -101,6 +137,10 @@ class BASE_EXPORT PlatformThread {
// Gets the current thread id, which may be useful for logging purposes.
static PlatformThreadId CurrentId();
+ // Gets the current thread reference, which can be used to check if
+ // we're on the right thread quickly.
+ static PlatformThreadRef CurrentRef();
+
// Get the current handle.
static PlatformThreadHandle CurrentHandle();
diff --git a/chromium/base/threading/platform_thread_android.cc b/chromium/base/threading/platform_thread_android.cc
index 28026350b66..bd0b4b33db7 100644
--- a/chromium/base/threading/platform_thread_android.cc
+++ b/chromium/base/threading/platform_thread_android.cc
@@ -5,6 +5,7 @@
#include "base/threading/platform_thread.h"
#include <errno.h>
+#include <sys/prctl.h>
#include <sys/resource.h>
#include "base/android/jni_android.h"
@@ -77,6 +78,18 @@ void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
void PlatformThread::SetName(const char* name) {
ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
tracked_objects::ThreadData::InitializeThreadContext(name);
+
+ // Like linux, on android we can get the thread names to show up in the
+ // debugger by setting the process name for the LWP.
+ // We don't want to do this for the main thread because that would rename
+ // the process, causing tools like killall to stop working.
+ if (PlatformThread::CurrentId() == getpid())
+ return;
+
+ // Set the name for the LWP (which gets truncated to 15 characters).
+ int err = prctl(PR_SET_NAME, name);
+ if (err < 0 && errno != EPERM)
+ DPLOG(ERROR) << "prctl(PR_SET_NAME)";
}
@@ -95,7 +108,13 @@ void TerminateOnThread() {
}
size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(ADDRESS_SANITIZER)
return 0;
+#else
+ // AddressSanitizer bloats the stack approximately 2x. Default stack size of
+ // 1Mb is not enough for some tests (see http://crbug.com/263749 for example).
+ return 2 * (1 << 20); // 2Mb
+#endif
}
bool RegisterThreadUtils(JNIEnv* env) {
diff --git a/chromium/base/threading/platform_thread_freebsd.cc b/chromium/base/threading/platform_thread_freebsd.cc
new file mode 100644
index 00000000000..7a24f4e055e
--- /dev/null
+++ b/chromium/base/threading/platform_thread_freebsd.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/platform_thread.h"
+
+#include <errno.h>
+#include <sched.h>
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/safe_strerror_posix.h"
+#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/tracked_objects.h"
+
+#if !defined(OS_NACL)
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#endif
+
+namespace base {
+
+namespace {
+int ThreadNiceValue(ThreadPriority priority) {
+ switch (priority) {
+ case kThreadPriority_RealtimeAudio:
+ return -10;
+ case kThreadPriority_Background:
+ return 10;
+ case kThreadPriority_Normal:
+ return 0;
+ case kThreadPriority_Display:
+ return -6;
+ default:
+ NOTREACHED() << "Unknown priority.";
+ return 0;
+ }
+}
+} // namespace
+
+// static
+void PlatformThread::SetName(const char* name) {
+ ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
+ tracked_objects::ThreadData::InitializeThreadContext(name);
+
+#if !defined(OS_NACL)
+ // On FreeBSD we can get the thread names to show up in the debugger by
+ // setting the process name for the LWP. We don't want to do this for the
+ // main thread because that would rename the process, causing tools like
+ // killall to stop working.
+ if (PlatformThread::CurrentId() == getpid())
+ return;
+ setproctitle("%s", name);
+#endif // !defined(OS_NACL)
+}
+
+// static
+void PlatformThread::SetThreadPriority(PlatformThreadHandle handle,
+ ThreadPriority priority) {
+#if !defined(OS_NACL)
+ if (priority == kThreadPriority_RealtimeAudio) {
+ const struct sched_param kRealTimePrio = { 8 };
+ if (pthread_setschedparam(pthread_self(), SCHED_RR, &kRealTimePrio) == 0) {
+ // Got real time priority, no need to set nice level.
+ return;
+ }
+ }
+
+ // setpriority(2) will set a thread's priority if it is passed a tid as
+ // the 'process identifier', not affecting the rest of the threads in the
+ // process. Setting this priority will only succeed if the user has been
+ // granted permission to adjust nice values on the system.
+ DCHECK_NE(handle.id_, kInvalidThreadId);
+ const int kNiceSetting = ThreadNiceValue(priority);
+ if (setpriority(PRIO_PROCESS, handle.id_, kNiceSetting)) {
+ DVPLOG(1) << "Failed to set nice value of thread ("
+ << handle.id_ << ") to " << kNiceSetting;
+ }
+#endif // !defined(OS_NACL)
+}
+
+void InitThreading() {}
+
+void InitOnThread() {}
+
+void TerminateOnThread() {}
+
+size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(THREAD_SANITIZER)
+ return 0;
+#else
+ // ThreadSanitizer bloats the stack heavily. Evidence has been that the
+ // default stack size isn't enough for some browser tests.
+ return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux).
+#endif
+}
+
+} // namespace base
diff --git a/chromium/base/threading/platform_thread_linux.cc b/chromium/base/threading/platform_thread_linux.cc
index 6d46de4cf26..636e13331c4 100644
--- a/chromium/base/threading/platform_thread_linux.cc
+++ b/chromium/base/threading/platform_thread_linux.cc
@@ -26,6 +26,7 @@
namespace base {
namespace {
+
int ThreadNiceValue(ThreadPriority priority) {
switch (priority) {
case kThreadPriority_RealtimeAudio:
@@ -41,7 +42,8 @@ int ThreadNiceValue(ThreadPriority priority) {
return 0;
}
}
-} // namespace
+
+} // namespace
// static
void PlatformThread::SetName(const char* name) {
@@ -100,7 +102,14 @@ void InitOnThread() {}
void TerminateOnThread() {}
size_t GetDefaultThreadStackSize(const pthread_attr_t& attributes) {
+#if !defined(THREAD_SANITIZER) && !defined(MEMORY_SANITIZER)
return 0;
+#else
+ // ThreadSanitizer bloats the stack heavily. Evidence has been that the
+ // default stack size isn't enough for some browser tests.
+ // MemorySanitizer needs this as a temporary fix for http://crbug.com/353687
+ return 2 * (1 << 23); // 2 times 8192K (the default stack size on Linux).
+#endif
}
} // namespace base
diff --git a/chromium/base/threading/platform_thread_mac.mm b/chromium/base/threading/platform_thread_mac.mm
index d81a286cb2c..147e625dbc8 100644
--- a/chromium/base/threading/platform_thread_mac.mm
+++ b/chromium/base/threading/platform_thread_mac.mm
@@ -5,15 +5,16 @@
#include "base/threading/platform_thread.h"
#import <Foundation/Foundation.h>
-#include <algorithm>
-#include <dlfcn.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mach/thread_policy.h>
#include <sys/resource.h>
+#include <algorithm>
+
#include "base/lazy_instance.h"
#include "base/logging.h"
+#include "base/mac/mach_logging.h"
#include "base/threading/thread_id_name_manager.h"
#include "base/tracked_objects.h"
@@ -45,21 +46,13 @@ void PlatformThread::SetName(const char* name) {
ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name);
tracked_objects::ThreadData::InitializeThreadContext(name);
- // pthread_setname_np is only available in 10.6 or later, so test
- // for it at runtime.
- int (*dynamic_pthread_setname_np)(const char*);
- *reinterpret_cast<void**>(&dynamic_pthread_setname_np) =
- dlsym(RTLD_DEFAULT, "pthread_setname_np");
- if (!dynamic_pthread_setname_np)
- return;
-
// Mac OS X does not expose the length limit of the name, so
// hardcode it.
const int kMaxNameLength = 63;
std::string shortened_name = std::string(name).substr(0, kMaxNameLength);
// pthread_setname() fails (harmlessly) in the sandbox, ignore when it does.
// See http://crbug.com/47058
- dynamic_pthread_setname_np(shortened_name.c_str());
+ pthread_setname_np(shortened_name.c_str());
}
namespace {
@@ -69,20 +62,19 @@ void SetPriorityNormal(mach_port_t mach_thread_id) {
// Please note that this call could fail in rare cases depending
// on runtime conditions.
thread_standard_policy policy;
- kern_return_t result = thread_policy_set(mach_thread_id,
- THREAD_STANDARD_POLICY,
- (thread_policy_t)&policy,
- THREAD_STANDARD_POLICY_COUNT);
+ kern_return_t result =
+ thread_policy_set(mach_thread_id,
+ THREAD_STANDARD_POLICY,
+ reinterpret_cast<thread_policy_t>(&policy),
+ THREAD_STANDARD_POLICY_COUNT);
if (result != KERN_SUCCESS)
- DVLOG(1) << "thread_policy_set() failure: " << result;
+ MACH_DVLOG(1, result) << "thread_policy_set";
}
// Enables time-contraint policy and priority suitable for low-latency,
// glitch-resistant audio.
void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
- kern_return_t result;
-
// Increase thread priority to real-time.
// Please note that the thread_policy_set() calls may fail in
@@ -93,12 +85,13 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
// Make thread fixed priority.
thread_extended_policy_data_t policy;
policy.timeshare = 0; // Set to 1 for a non-fixed thread.
- result = thread_policy_set(mach_thread_id,
- THREAD_EXTENDED_POLICY,
- (thread_policy_t)&policy,
- THREAD_EXTENDED_POLICY_COUNT);
+ kern_return_t result =
+ thread_policy_set(mach_thread_id,
+ THREAD_EXTENDED_POLICY,
+ reinterpret_cast<thread_policy_t>(&policy),
+ THREAD_EXTENDED_POLICY_COUNT);
if (result != KERN_SUCCESS) {
- DVLOG(1) << "thread_policy_set() failure: " << result;
+ MACH_DVLOG(1, result) << "thread_policy_set";
return;
}
@@ -107,10 +100,10 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
precedence.importance = 63;
result = thread_policy_set(mach_thread_id,
THREAD_PRECEDENCE_POLICY,
- (thread_policy_t)&precedence,
+ reinterpret_cast<thread_policy_t>(&precedence),
THREAD_PRECEDENCE_POLICY_COUNT);
if (result != KERN_SUCCESS) {
- DVLOG(1) << "thread_policy_set() failure: " << result;
+ MACH_DVLOG(1, result) << "thread_policy_set";
return;
}
@@ -141,7 +134,7 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
mach_timebase_info_data_t tb_info;
mach_timebase_info(&tb_info);
double ms_to_abs_time =
- ((double)tb_info.denom / (double)tb_info.numer) * 1000000;
+ (static_cast<double>(tb_info.denom) / tb_info.numer) * 1000000;
thread_time_constraint_policy_data_t time_constraints;
time_constraints.period = kTimeQuantum * ms_to_abs_time;
@@ -149,12 +142,12 @@ void SetPriorityRealtimeAudio(mach_port_t mach_thread_id) {
time_constraints.constraint = kMaxTimeAllowed * ms_to_abs_time;
time_constraints.preemptible = 0;
- result = thread_policy_set(mach_thread_id,
- THREAD_TIME_CONSTRAINT_POLICY,
- (thread_policy_t)&time_constraints,
- THREAD_TIME_CONSTRAINT_POLICY_COUNT);
- if (result != KERN_SUCCESS)
- DVLOG(1) << "thread_policy_set() failure: " << result;
+ result =
+ thread_policy_set(mach_thread_id,
+ THREAD_TIME_CONSTRAINT_POLICY,
+ reinterpret_cast<thread_policy_t>(&time_constraints),
+ THREAD_TIME_CONSTRAINT_POLICY_COUNT);
+ MACH_DVLOG_IF(1, result != KERN_SUCCESS, result) << "thread_policy_set";
return;
}
diff --git a/chromium/base/threading/platform_thread_posix.cc b/chromium/base/threading/platform_thread_posix.cc
index 392d4613369..42b416d4a38 100644
--- a/chromium/base/threading/platform_thread_posix.cc
+++ b/chromium/base/threading/platform_thread_posix.cc
@@ -116,13 +116,15 @@ bool CreateThread(size_t stack_size, bool joinable,
params.priority = priority;
params.handle = thread_handle;
- pthread_t handle = 0;
+ pthread_t handle;
int err = pthread_create(&handle,
&attributes,
ThreadFunc,
&params);
success = !err;
if (!success) {
+ // Value of |handle| is undefined if pthread_create fails.
+ handle = 0;
errno = err;
PLOG(ERROR) << "pthread_create";
}
@@ -150,7 +152,7 @@ PlatformThreadId PlatformThread::CurrentId() {
return syscall(__NR_gettid);
#elif defined(OS_ANDROID)
return gettid();
-#elif defined(OS_SOLARIS)
+#elif defined(OS_SOLARIS) || defined(OS_QNX)
return pthread_self();
#elif defined(OS_NACL) && defined(__GLIBC__)
return pthread_self();
@@ -162,7 +164,12 @@ PlatformThreadId PlatformThread::CurrentId() {
#endif
}
-//static
+// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(pthread_self());
+}
+
+// static
PlatformThreadHandle PlatformThread::CurrentHandle() {
return PlatformThreadHandle(pthread_self(), CurrentId());
}
diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc
index e9752ba2139..80f353a8b46 100644
--- a/chromium/base/threading/platform_thread_win.cc
+++ b/chromium/base/threading/platform_thread_win.cc
@@ -10,7 +10,7 @@
#include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h"
#include "base/tracked_objects.h"
-
+#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
namespace base {
@@ -54,28 +54,35 @@ DWORD __stdcall ThreadFunc(void* params) {
if (!thread_params->joinable)
base::ThreadRestrictions::SetSingletonAllowed(false);
- /* Retrieve a copy of the thread handle to use as the key in the
- * thread name mapping. */
+ // Retrieve a copy of the thread handle to use as the key in the
+ // thread name mapping.
PlatformThreadHandle::Handle platform_handle;
- DuplicateHandle(
- GetCurrentProcess(),
- GetCurrentThread(),
- GetCurrentProcess(),
- &platform_handle,
- 0,
- FALSE,
- DUPLICATE_SAME_ACCESS);
-
- ThreadIdNameManager::GetInstance()->RegisterThread(
- platform_handle,
- PlatformThread::CurrentId());
+ BOOL did_dup = DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &platform_handle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS);
+
+ win::ScopedHandle scoped_platform_handle;
+
+ if (did_dup) {
+ scoped_platform_handle.Set(platform_handle);
+ ThreadIdNameManager::GetInstance()->RegisterThread(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
delete thread_params;
delegate->ThreadMain();
- ThreadIdNameManager::GetInstance()->RemoveName(
- platform_handle,
- PlatformThread::CurrentId());
+ if (did_dup) {
+ ThreadIdNameManager::GetInstance()->RemoveName(
+ scoped_platform_handle.Get(),
+ PlatformThread::CurrentId());
+ }
+
return NULL;
}
@@ -123,6 +130,11 @@ PlatformThreadId PlatformThread::CurrentId() {
}
// static
+PlatformThreadRef PlatformThread::CurrentRef() {
+ return PlatformThreadRef(GetCurrentThreadId());
+}
+
+// static
PlatformThreadHandle PlatformThread::CurrentHandle() {
NOTIMPLEMENTED(); // See OpenThread()
return PlatformThreadHandle();
diff --git a/chromium/base/threading/sequenced_worker_pool.cc b/chromium/base/threading/sequenced_worker_pool.cc
index 4fc090d1186..532f26ebe5c 100644
--- a/chromium/base/threading/sequenced_worker_pool.cc
+++ b/chromium/base/threading/sequenced_worker_pool.cc
@@ -32,6 +32,8 @@
#if defined(OS_MACOSX)
#include "base/mac/scoped_nsautorelease_pool.h"
+#elif defined(OS_WIN)
+#include "base/win/scoped_com_initializer.h"
#endif
#if !defined(OS_NACL)
@@ -480,8 +482,7 @@ SequencedWorkerPool::Worker::Worker(
const scoped_refptr<SequencedWorkerPool>& worker_pool,
int thread_number,
const std::string& prefix)
- : SimpleThread(
- prefix + StringPrintf("Worker%d", thread_number).c_str()),
+ : SimpleThread(prefix + StringPrintf("Worker%d", thread_number)),
worker_pool_(worker_pool),
running_shutdown_behavior_(CONTINUE_ON_SHUTDOWN) {
Start();
@@ -491,6 +492,10 @@ SequencedWorkerPool::Worker::~Worker() {
}
void SequencedWorkerPool::Worker::Run() {
+#if defined(OS_WIN)
+ win::ScopedCOMInitializer com_initializer;
+#endif
+
// Store a pointer to the running sequence in thread local storage for
// static function access.
g_lazy_tls_ptr.Get().Set(&running_sequence_);
@@ -593,7 +598,8 @@ bool SequencedWorkerPool::Inner::PostTask(
// The trace_id is used for identifying the task in about:tracing.
sequenced.trace_id = trace_id_++;
- TRACE_EVENT_FLOW_BEGIN0("task", "SequencedWorkerPool::PostTask",
+ TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(sequenced, static_cast<void*>(this))));
sequenced.sequence_task_number = LockedGetNextSequenceTaskNumber();
@@ -726,9 +732,10 @@ void SequencedWorkerPool::Inner::ThreadLoop(Worker* this_worker) {
GetWorkStatus status =
GetWork(&task, &wait_time, &delete_these_outside_lock);
if (status == GET_WORK_FOUND) {
- TRACE_EVENT_FLOW_END0("task", "SequencedWorkerPool::PostTask",
+ TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
+ "SequencedWorkerPool::PostTask",
TRACE_ID_MANGLE(GetTaskTraceID(task, static_cast<void*>(this))));
- TRACE_EVENT2("task", "SequencedWorkerPool::ThreadLoop",
+ TRACE_EVENT2("toplevel", "SequencedWorkerPool::ThreadLoop",
"src_file", task.posted_from.file_name(),
"src_func", task.posted_from.function_name());
int new_thread_id = WillRunWorkerTask(task);
diff --git a/chromium/base/threading/sequenced_worker_pool_unittest.cc b/chromium/base/threading/sequenced_worker_pool_unittest.cc
index a07fd479095..10cf28ba30d 100644
--- a/chromium/base/threading/sequenced_worker_pool_unittest.cc
+++ b/chromium/base/threading/sequenced_worker_pool_unittest.cc
@@ -832,10 +832,6 @@ class SequencedWorkerPoolTaskRunnerTestDelegate {
// reference to the pool.
}
- bool TaskRunnerHandlesNonZeroDelays() const {
- return true;
- }
-
private:
MessageLoop message_loop_;
scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
@@ -872,10 +868,6 @@ class SequencedWorkerPoolTaskRunnerWithShutdownBehaviorTestDelegate {
// reference to the pool.
}
- bool TaskRunnerHandlesNonZeroDelays() const {
- return true;
- }
-
private:
MessageLoop message_loop_;
scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
@@ -913,10 +905,6 @@ class SequencedWorkerPoolSequencedTaskRunnerTestDelegate {
// reference to the pool.
}
- bool TaskRunnerHandlesNonZeroDelays() const {
- return true;
- }
-
private:
MessageLoop message_loop_;
scoped_ptr<SequencedWorkerPoolOwner> pool_owner_;
diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc
index ae4d3731947..7877b193548 100644
--- a/chromium/base/threading/thread.cc
+++ b/chromium/base/threading/thread.cc
@@ -63,7 +63,7 @@ Thread::Options::Options(MessageLoop::Type type,
Thread::Options::~Options() {
}
-Thread::Thread(const char* name)
+Thread::Thread(const std::string& name)
:
#if defined(OS_WIN)
com_status_(NONE),
diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h
index 99f9dd4a597..09a6fa5c505 100644
--- a/chromium/base/threading/thread.h
+++ b/chromium/base/threading/thread.h
@@ -58,7 +58,7 @@ class BASE_EXPORT Thread : PlatformThread::Delegate {
// Constructor.
// name is a display string to identify the thread.
- explicit Thread(const char* name);
+ explicit Thread(const std::string& name);
// Destroys the thread, stopping it if necessary.
//
diff --git a/chromium/base/threading/thread_checker.h b/chromium/base/threading/thread_checker.h
index a2ab3fe4fab..4d71f798d4d 100644
--- a/chromium/base/threading/thread_checker.h
+++ b/chromium/base/threading/thread_checker.h
@@ -20,9 +20,7 @@
#define ENABLE_THREAD_CHECKER 0
#endif
-#if ENABLE_THREAD_CHECKER
#include "base/threading/thread_checker_impl.h"
-#endif
namespace base {
diff --git a/chromium/base/threading/thread_checker_impl.cc b/chromium/base/threading/thread_checker_impl.cc
index 985433e5f11..eb87bae772c 100644
--- a/chromium/base/threading/thread_checker_impl.cc
+++ b/chromium/base/threading/thread_checker_impl.cc
@@ -7,7 +7,7 @@
namespace base {
ThreadCheckerImpl::ThreadCheckerImpl()
- : valid_thread_id_(kInvalidThreadId) {
+ : valid_thread_id_() {
EnsureThreadIdAssigned();
}
@@ -16,19 +16,19 @@ ThreadCheckerImpl::~ThreadCheckerImpl() {}
bool ThreadCheckerImpl::CalledOnValidThread() const {
EnsureThreadIdAssigned();
AutoLock auto_lock(lock_);
- return valid_thread_id_ == PlatformThread::CurrentId();
+ return valid_thread_id_ == PlatformThread::CurrentRef();
}
void ThreadCheckerImpl::DetachFromThread() {
AutoLock auto_lock(lock_);
- valid_thread_id_ = kInvalidThreadId;
+ valid_thread_id_ = PlatformThreadRef();
}
void ThreadCheckerImpl::EnsureThreadIdAssigned() const {
AutoLock auto_lock(lock_);
- if (valid_thread_id_ != kInvalidThreadId)
- return;
- valid_thread_id_ = PlatformThread::CurrentId();
+ if (valid_thread_id_.is_null()) {
+ valid_thread_id_ = PlatformThread::CurrentRef();
+ }
}
} // namespace base
diff --git a/chromium/base/threading/thread_checker_impl.h b/chromium/base/threading/thread_checker_impl.h
index 24361c83221..879ac3ab354 100644
--- a/chromium/base/threading/thread_checker_impl.h
+++ b/chromium/base/threading/thread_checker_impl.h
@@ -35,7 +35,7 @@ class BASE_EXPORT ThreadCheckerImpl {
mutable base::Lock lock_;
// This is mutable so that CalledOnValidThread can set it.
// It's guarded by |lock_|.
- mutable PlatformThreadId valid_thread_id_;
+ mutable PlatformThreadRef valid_thread_id_;
};
} // namespace base
diff --git a/chromium/base/threading/thread_collision_warner.h b/chromium/base/threading/thread_collision_warner.h
index d509a58e8de..5172b2e74e6 100644
--- a/chromium/base/threading/thread_collision_warner.h
+++ b/chromium/base/threading/thread_collision_warner.h
@@ -9,6 +9,7 @@
#include "base/atomicops.h"
#include "base/base_export.h"
+#include "base/basictypes.h"
#include "base/compiler_specific.h"
// A helper class alongside macros to be used to verify assumptions about thread
diff --git a/chromium/base/threading/thread_local.h b/chromium/base/threading/thread_local.h
index b13be1ab467..df9c4b72573 100644
--- a/chromium/base/threading/thread_local.h
+++ b/chromium/base/threading/thread_local.h
@@ -26,6 +26,9 @@
// you must of course properly deal with safety and race conditions. This
// means a function-level static initializer is generally inappropiate.
//
+// In Android, the system TLS is limited, the implementation is backed with
+// ThreadLocalStorage.
+//
// Example usage:
// // My class is logically attached to a single thread. We cache a pointer
// // on the thread it was created on, so we can implement current().
@@ -50,6 +53,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
+#include "base/threading/thread_local_storage.h"
#if defined(OS_POSIX)
#include <pthread.h>
@@ -62,6 +66,8 @@ namespace internal {
struct BASE_EXPORT ThreadLocalPlatform {
#if defined(OS_WIN)
typedef unsigned long SlotType;
+#elif defined(OS_ANDROID)
+ typedef ThreadLocalStorage::StaticSlot SlotType;
#elif defined(OS_POSIX)
typedef pthread_key_t SlotType;
#endif
diff --git a/chromium/base/threading/thread_local_android.cc b/chromium/base/threading/thread_local_android.cc
new file mode 100644
index 00000000000..c890237f67d
--- /dev/null
+++ b/chromium/base/threading/thread_local_android.cc
@@ -0,0 +1,34 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local.h"
+
+#include "base/logging.h"
+
+namespace base {
+namespace internal {
+
+// static
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ bool succeed = slot->Initialize(NULL);
+ CHECK(succeed);
+}
+
+// static
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
+ slot.Free();
+}
+
+// static
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
+ return slot.Get();
+}
+
+// static
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
+ slot.Set(value);
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/threading/thread_local_posix.cc b/chromium/base/threading/thread_local_posix.cc
index 526a66d0629..75ea4795d45 100644
--- a/chromium/base/threading/thread_local_posix.cc
+++ b/chromium/base/threading/thread_local_posix.cc
@@ -8,6 +8,8 @@
#include "base/logging.h"
+#if !defined(OS_ANDROID)
+
namespace base {
namespace internal {
@@ -36,3 +38,5 @@ void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
} // namespace internal
} // namespace base
+
+#endif // !defined(OS_ANDROID)
diff --git a/chromium/base/threading/thread_local_storage.cc b/chromium/base/threading/thread_local_storage.cc
new file mode 100644
index 00000000000..e1749c467a8
--- /dev/null
+++ b/chromium/base/threading/thread_local_storage.cc
@@ -0,0 +1,250 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/threading/thread_local_storage.h"
+
+#include "base/atomicops.h"
+#include "base/logging.h"
+
+using base::internal::PlatformThreadLocalStorage;
+
+namespace {
+// In order to make TLS destructors work, we need to keep around a function
+// pointer to the destructor for each slot. We keep this array of pointers in a
+// global (static) array.
+// We use the single OS-level TLS slot (giving us one pointer per thread) to
+// hold a pointer to a per-thread array (table) of slots that we allocate to
+// Chromium consumers.
+
+// g_native_tls_key is the one native TLS that we use. It stores our table.
+base::subtle::AtomicWord g_native_tls_key =
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES;
+
+// g_last_used_tls_key is the high-water-mark of allocated thread local storage.
+// Each allocation is an index into our g_tls_destructors[]. Each such index is
+// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot
+// instance. We reserve the value slot_ == 0 to indicate that the corresponding
+// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called,
+// etc.). This reserved use of 0 is then stated as the initial value of
+// g_last_used_tls_key, so that the first issued index will be 1.
+base::subtle::Atomic32 g_last_used_tls_key = 0;
+
+// The maximum number of 'slots' in our thread local storage stack.
+const int kThreadLocalStorageSize = 256;
+
+// The maximum number of times to try to clear slots by calling destructors.
+// Use pthread naming convention for clarity.
+const int kMaxDestructorIterations = kThreadLocalStorageSize;
+
+// An array of destructor function pointers for the slots. If a slot has a
+// destructor, it will be stored in its corresponding entry in this array.
+// The elements are volatile to ensure that when the compiler reads the value
+// to potentially call the destructor, it does so once, and that value is tested
+// for null-ness and then used. Yes, that would be a weird de-optimization,
+// but I can imagine some register machines where it was just as easy to
+// re-fetch an array element, and I want to be sure a call to free the key
+// (i.e., null out the destructor entry) that happens on a separate thread can't
+// hurt the racy calls to the destructors on another thread.
+volatile base::ThreadLocalStorage::TLSDestructorFunc
+ g_tls_destructors[kThreadLocalStorageSize];
+
+// This function is called to initialize our entire Chromium TLS system.
+// It may be called very early, and we need to complete most all of the setup
+// (initialization) before calling *any* memory allocator functions, which may
+// recursively depend on this initialization.
+// As a result, we use Atomics, and avoid anything (like a singleton) that might
+// require memory allocations.
+void** ConstructTlsVector() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key));
+
+ // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or
+ // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we
+ // define an almost impossible value be it.
+ // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc
+ // another TLS slot.
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
+ PlatformThreadLocalStorage::TLSKey tmp = key;
+ CHECK(PlatformThreadLocalStorage::AllocTLS(&key) &&
+ key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES);
+ PlatformThreadLocalStorage::FreeTLS(tmp);
+ }
+ // Atomically test-and-set the tls_key. If the key is
+ // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as
+ // another thread already did our dirty work.
+ if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES !=
+ base::subtle::NoBarrier_CompareAndSwap(&g_native_tls_key,
+ PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key)) {
+ // We've been shortcut. Another thread replaced g_native_tls_key first so
+ // we need to destroy our index and use the one the other thread got
+ // first.
+ PlatformThreadLocalStorage::FreeTLS(key);
+ key = base::subtle::NoBarrier_Load(&g_native_tls_key);
+ }
+ }
+ CHECK(!PlatformThreadLocalStorage::GetTLSValue(key));
+
+ // Some allocators, such as TCMalloc, make use of thread local storage.
+ // As a result, any attempt to call new (or malloc) will lazily cause such a
+ // system to initialize, which will include registering for a TLS key. If we
+ // are not careful here, then that request to create a key will call new back,
+ // and we'll have an infinite loop. We avoid that as follows:
+ // Use a stack allocated vector, so that we don't have dependence on our
+ // allocator until our service is in place. (i.e., don't even call new until
+ // after we're setup)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
+ memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
+ // Ensure that any rentrant calls change the temp version.
+ PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
+
+ // Allocate an array to store our data.
+ void** tls_data = new void*[kThreadLocalStorageSize];
+ memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
+ PlatformThreadLocalStorage::SetTLSValue(key, tls_data);
+ return tls_data;
+}
+
+void OnThreadExitInternal(void* value) {
+ DCHECK(value);
+ void** tls_data = static_cast<void**>(value);
+ // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
+ // terminates, one of the destructor calls we make may be to shut down an
+ // allocator. We have to be careful that after we've shutdown all of the
+ // known destructors (perchance including an allocator), that we don't call
+ // the allocator and cause it to resurrect itself (with no possibly destructor
+ // call to follow). We handle this problem as follows:
+ // Switch to using a stack allocated vector, so that we don't have dependence
+ // on our allocator after we have called all g_tls_destructors. (i.e., don't
+ // even call delete[] after we're done with destructors.)
+ void* stack_allocated_tls_data[kThreadLocalStorageSize];
+ memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
+ // Ensure that any re-entrant calls change the temp version.
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
+ delete[] tls_data; // Our last dependence on an allocator.
+
+ int remaining_attempts = kMaxDestructorIterations;
+ bool need_to_scan_destructors = true;
+ while (need_to_scan_destructors) {
+ need_to_scan_destructors = false;
+ // Try to destroy the first-created-slot (which is slot 1) in our last
+ // destructor call. That user was able to function, and define a slot with
+ // no other services running, so perhaps it is a basic service (like an
+ // allocator) and should also be destroyed last. If we get the order wrong,
+ // then we'll itterate several more times, so it is really not that
+ // critical (but it might help).
+ base::subtle::Atomic32 last_used_tls_key =
+ base::subtle::NoBarrier_Load(&g_last_used_tls_key);
+ for (int slot = last_used_tls_key; slot > 0; --slot) {
+ void* value = stack_allocated_tls_data[slot];
+ if (value == NULL)
+ continue;
+
+ base::ThreadLocalStorage::TLSDestructorFunc destructor =
+ g_tls_destructors[slot];
+ if (destructor == NULL)
+ continue;
+ stack_allocated_tls_data[slot] = NULL; // pre-clear the slot.
+ destructor(value);
+ // Any destructor might have called a different service, which then set
+ // a different slot to a non-NULL value. Hence we need to check
+ // the whole vector again. This is a pthread standard.
+ need_to_scan_destructors = true;
+ }
+ if (--remaining_attempts <= 0) {
+ NOTREACHED(); // Destructors might not have been called.
+ break;
+ }
+ }
+
+ // Remove our stack allocated vector.
+ PlatformThreadLocalStorage::SetTLSValue(key, NULL);
+}
+
+} // namespace
+
+namespace base {
+
+namespace internal {
+
+#if defined(OS_WIN)
+void PlatformThreadLocalStorage::OnThreadExit() {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
+ return;
+ void *tls_data = GetTLSValue(key);
+ // Maybe we have never initialized TLS for this thread.
+ if (!tls_data)
+ return;
+ OnThreadExitInternal(tls_data);
+}
+#elif defined(OS_POSIX)
+void PlatformThreadLocalStorage::OnThreadExit(void* value) {
+ OnThreadExitInternal(value);
+}
+#endif // defined(OS_WIN)
+
+} // namespace internal
+
+ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
+ initialized_ = false;
+ slot_ = 0;
+ Initialize(destructor);
+}
+
+bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
+ PlatformThreadLocalStorage::TLSKey key =
+ base::subtle::NoBarrier_Load(&g_native_tls_key);
+ if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES ||
+ !PlatformThreadLocalStorage::GetTLSValue(key))
+ ConstructTlsVector();
+
+ // Grab a new slot.
+ slot_ = base::subtle::NoBarrier_AtomicIncrement(&g_last_used_tls_key, 1);
+ DCHECK_GT(slot_, 0);
+ CHECK_LT(slot_, kThreadLocalStorageSize);
+
+ // Setup our destructor.
+ g_tls_destructors[slot_] = destructor;
+ initialized_ = true;
+ return true;
+}
+
+void ThreadLocalStorage::StaticSlot::Free() {
+ // At this time, we don't reclaim old indices for TLS slots.
+ // So all we need to do is wipe the destructor.
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ g_tls_destructors[slot_] = NULL;
+ slot_ = 0;
+ initialized_ = false;
+}
+
+void* ThreadLocalStorage::StaticSlot::Get() const {
+ void** tls_data = static_cast<void**>(
+ PlatformThreadLocalStorage::GetTLSValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key)));
+ if (!tls_data)
+ tls_data = ConstructTlsVector();
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ return tls_data[slot_];
+}
+
+void ThreadLocalStorage::StaticSlot::Set(void* value) {
+ void** tls_data = static_cast<void**>(
+ PlatformThreadLocalStorage::GetTLSValue(
+ base::subtle::NoBarrier_Load(&g_native_tls_key)));
+ if (!tls_data)
+ tls_data = ConstructTlsVector();
+ DCHECK_GT(slot_, 0);
+ DCHECK_LT(slot_, kThreadLocalStorageSize);
+ tls_data[slot_] = value;
+}
+
+} // namespace base
diff --git a/chromium/base/threading/thread_local_storage.h b/chromium/base/threading/thread_local_storage.h
index eb5648f76c9..53ebe55e51c 100644
--- a/chromium/base/threading/thread_local_storage.h
+++ b/chromium/base/threading/thread_local_storage.h
@@ -8,12 +8,71 @@
#include "base/base_export.h"
#include "base/basictypes.h"
-#if defined(OS_POSIX)
+#if defined(OS_WIN)
+#include <windows.h>
+#elif defined(OS_POSIX)
#include <pthread.h>
#endif
namespace base {
+namespace internal {
+
+// WARNING: You should *NOT* be using this class directly.
+// PlatformThreadLocalStorage is low-level abstraction to the OS's TLS
+// interface, you should instead be using ThreadLocalStorage::StaticSlot/Slot.
+class BASE_EXPORT PlatformThreadLocalStorage {
+ public:
+
+#if defined(OS_WIN)
+ typedef unsigned long TLSKey;
+ enum { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES };
+#elif defined(OS_POSIX)
+ typedef pthread_key_t TLSKey;
+ // The following is a "reserved key" which is used in our generic Chromium
+ // ThreadLocalStorage implementation. We expect that an OS will not return
+ // such a key, but if it is returned (i.e., the OS tries to allocate it) we
+ // will just request another key.
+ enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF };
+#endif
+
+ // The following methods need to be supported on each OS platform, so that
+ // the Chromium ThreadLocalStore functionality can be constructed.
+ // Chromium will use these methods to acquire a single OS slot, and then use
+ // that to support a much larger number of Chromium slots (independent of the
+ // OS restrictions).
+ // The following returns true if it successfully is able to return an OS
+ // key in |key|.
+ static bool AllocTLS(TLSKey* key);
+ // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS
+ // might not reuse released slot, you might just reset the TLS value with
+ // SetTLSValue().
+ static void FreeTLS(TLSKey key);
+ static void SetTLSValue(TLSKey key, void* value);
+ static void* GetTLSValue(TLSKey key);
+
+ // Each platform (OS implementation) is required to call this method on each
+ // terminating thread when the thread is about to terminate. This method
+ // will then call all registered destructors for slots in Chromium
+ // ThreadLocalStorage, until there are no slot values remaining as having
+ // been set on this thread.
+ // Destructors may end up being called multiple times on a terminating
+ // thread, as other destructors may re-set slots that were previously
+ // destroyed.
+#if defined(OS_WIN)
+ // Since Windows which doesn't support TLS destructor, the implementation
+ // should use GetTLSValue() to retrieve the value of TLS slot.
+ static void OnThreadExit();
+#elif defined(OS_POSIX)
+ // |Value| is the data stored in TLS slot, The implementation can't use
+ // GetTLSValue() to retrieve the value of slot as it has already been reset
+ // in Posix.
+ static void OnThreadExit(void* value);
+#endif
+};
+
+} // namespace internal
+
// Wrapper for thread local storage. This class doesn't do much except provide
// an API for portability.
class BASE_EXPORT ThreadLocalStorage {
@@ -60,12 +119,7 @@ class BASE_EXPORT ThreadLocalStorage {
// The internals of this struct should be considered private.
bool initialized_;
-#if defined(OS_WIN)
int slot_;
-#elif defined(OS_POSIX)
- pthread_key_t key_;
-#endif
-
};
// A convenience wrapper around StaticSlot with a constructor. Can be used
@@ -77,11 +131,8 @@ class BASE_EXPORT ThreadLocalStorage {
private:
using StaticSlot::initialized_;
-#if defined(OS_WIN)
using StaticSlot::slot_;
-#elif defined(OS_POSIX)
- using StaticSlot::key_;
-#endif
+
DISALLOW_COPY_AND_ASSIGN(Slot);
};
diff --git a/chromium/base/threading/thread_local_storage_posix.cc b/chromium/base/threading/thread_local_storage_posix.cc
index 75da5a7d8f0..ebaf4005d33 100644
--- a/chromium/base/threading/thread_local_storage_posix.cc
+++ b/chromium/base/threading/thread_local_storage_posix.cc
@@ -8,42 +8,27 @@
namespace base {
-ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
- initialized_ = false;
- key_ = 0;
- Initialize(destructor);
-}
-
-bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
- DCHECK(!initialized_);
- int error = pthread_key_create(&key_, destructor);
- if (error) {
- NOTREACHED();
- return false;
- }
+namespace internal {
- initialized_ = true;
- return true;
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ return !pthread_key_create(key,
+ base::internal::PlatformThreadLocalStorage::OnThreadExit);
}
-void ThreadLocalStorage::StaticSlot::Free() {
- DCHECK(initialized_);
- int error = pthread_key_delete(key_);
- if (error)
- NOTREACHED();
- initialized_ = false;
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ int ret = pthread_key_delete(key);
+ DCHECK_EQ(ret, 0);
}
-void* ThreadLocalStorage::StaticSlot::Get() const {
- DCHECK(initialized_);
- return pthread_getspecific(key_);
+void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) {
+ return pthread_getspecific(key);
}
-void ThreadLocalStorage::StaticSlot::Set(void* value) {
- DCHECK(initialized_);
- int error = pthread_setspecific(key_, value);
- if (error)
- NOTREACHED();
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ int ret = pthread_setspecific(key, value);
+ DCHECK_EQ(ret, 0);
}
+} // namespace internal
+
} // namespace base
diff --git a/chromium/base/threading/thread_local_storage_win.cc b/chromium/base/threading/thread_local_storage_win.cc
index 0ae3cb4c8cd..42a7d016fdd 100644
--- a/chromium/base/threading/thread_local_storage_win.cc
+++ b/chromium/base/threading/thread_local_storage_win.cc
@@ -8,201 +8,35 @@
#include "base/logging.h"
-
-namespace {
-// In order to make TLS destructors work, we need to keep function
-// pointers to the destructor for each TLS that we allocate.
-// We make this work by allocating a single OS-level TLS, which
-// contains an array of slots for the application to use. In
-// parallel, we also allocate an array of destructors, which we
-// keep track of and call when threads terminate.
-
-// g_native_tls_key is the one native TLS that we use. It stores our table.
-long g_native_tls_key = TLS_OUT_OF_INDEXES;
-
-// g_last_used_tls_key is the high-water-mark of allocated thread local storage.
-// Each allocation is an index into our g_tls_destructors[]. Each such index is
-// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot
-// instance. We reserve the value slot_ == 0 to indicate that the corresponding
-// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called,
-// etc.). This reserved use of 0 is then stated as the initial value of
-// g_last_used_tls_key, so that the first issued index will be 1.
-long g_last_used_tls_key = 0;
-
-// The maximum number of 'slots' in our thread local storage stack.
-const int kThreadLocalStorageSize = 64;
-
-// The maximum number of times to try to clear slots by calling destructors.
-// Use pthread naming convention for clarity.
-const int kMaxDestructorIterations = kThreadLocalStorageSize;
-
-// An array of destructor function pointers for the slots. If a slot has a
-// destructor, it will be stored in its corresponding entry in this array.
-// The elements are volatile to ensure that when the compiler reads the value
-// to potentially call the destructor, it does so once, and that value is tested
-// for null-ness and then used. Yes, that would be a weird de-optimization,
-// but I can imagine some register machines where it was just as easy to
-// re-fetch an array element, and I want to be sure a call to free the key
-// (i.e., null out the destructor entry) that happens on a separate thread can't
-// hurt the racy calls to the destructors on another thread.
-volatile base::ThreadLocalStorage::TLSDestructorFunc
- g_tls_destructors[kThreadLocalStorageSize];
-
-void** ConstructTlsVector() {
- if (g_native_tls_key == TLS_OUT_OF_INDEXES) {
- long value = TlsAlloc();
- DCHECK(value != TLS_OUT_OF_INDEXES);
-
- // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES,
- // go ahead and set it. Otherwise, do nothing, as another
- // thread already did our dirty work.
- if (TLS_OUT_OF_INDEXES != InterlockedCompareExchange(
- &g_native_tls_key, value, TLS_OUT_OF_INDEXES)) {
- // We've been shortcut. Another thread replaced g_native_tls_key first so
- // we need to destroy our index and use the one the other thread got
- // first.
- TlsFree(value);
- }
- }
- DCHECK(!TlsGetValue(g_native_tls_key));
-
- // Some allocators, such as TCMalloc, make use of thread local storage.
- // As a result, any attempt to call new (or malloc) will lazily cause such a
- // system to initialize, which will include registering for a TLS key. If we
- // are not careful here, then that request to create a key will call new back,
- // and we'll have an infinite loop. We avoid that as follows:
- // Use a stack allocated vector, so that we don't have dependence on our
- // allocator until our service is in place. (i.e., don't even call new until
- // after we're setup)
- void* stack_allocated_tls_data[kThreadLocalStorageSize];
- memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
- // Ensure that any rentrant calls change the temp version.
- TlsSetValue(g_native_tls_key, stack_allocated_tls_data);
-
- // Allocate an array to store our data.
- void** tls_data = new void*[kThreadLocalStorageSize];
- memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
- TlsSetValue(g_native_tls_key, tls_data);
- return tls_data;
-}
-
-// Called when we terminate a thread, this function calls any TLS destructors
-// that are pending for this thread.
-void WinThreadExit() {
- if (g_native_tls_key == TLS_OUT_OF_INDEXES)
- return;
-
- void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
- // Maybe we have never initialized TLS for this thread.
- if (!tls_data)
- return;
-
- // Some allocators, such as TCMalloc, use TLS. As a result, when a thread
- // terminates, one of the destructor calls we make may be to shut down an
- // allocator. We have to be careful that after we've shutdown all of the
- // known destructors (perchance including an allocator), that we don't call
- // the allocator and cause it to resurrect itself (with no possibly destructor
- // call to follow). We handle this problem as follows:
- // Switch to using a stack allocated vector, so that we don't have dependence
- // on our allocator after we have called all g_tls_destructors. (i.e., don't
- // even call delete[] after we're done with destructors.)
- void* stack_allocated_tls_data[kThreadLocalStorageSize];
- memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
- // Ensure that any re-entrant calls change the temp version.
- TlsSetValue(g_native_tls_key, stack_allocated_tls_data);
- delete[] tls_data; // Our last dependence on an allocator.
-
- int remaining_attempts = kMaxDestructorIterations;
- bool need_to_scan_destructors = true;
- while (need_to_scan_destructors) {
- need_to_scan_destructors = false;
- // Try to destroy the first-created-slot (which is slot 1) in our last
- // destructor call. That user was able to function, and define a slot with
- // no other services running, so perhaps it is a basic service (like an
- // allocator) and should also be destroyed last. If we get the order wrong,
- // then we'll itterate several more times, so it is really not that
- // critical (but it might help).
- for (int slot = g_last_used_tls_key; slot > 0; --slot) {
- void* value = stack_allocated_tls_data[slot];
- if (value == NULL)
- continue;
- base::ThreadLocalStorage::TLSDestructorFunc destructor =
- g_tls_destructors[slot];
- if (destructor == NULL)
- continue;
- stack_allocated_tls_data[slot] = NULL; // pre-clear the slot.
- destructor(value);
- // Any destructor might have called a different service, which then set
- // a different slot to a non-NULL value. Hence we need to check
- // the whole vector again. This is a pthread standard.
- need_to_scan_destructors = true;
- }
- if (--remaining_attempts <= 0) {
- NOTREACHED(); // Destructors might not have been called.
- break;
- }
- }
-
- // Remove our stack allocated vector.
- TlsSetValue(g_native_tls_key, NULL);
-}
-
-} // namespace
-
namespace base {
-ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
- initialized_ = false;
- slot_ = 0;
- Initialize(destructor);
-}
-
-bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
- if (g_native_tls_key == TLS_OUT_OF_INDEXES || !TlsGetValue(g_native_tls_key))
- ConstructTlsVector();
+namespace internal {
- // Grab a new slot.
- slot_ = InterlockedIncrement(&g_last_used_tls_key);
- DCHECK_GT(slot_, 0);
- if (slot_ >= kThreadLocalStorageSize) {
- NOTREACHED();
- return false;
+bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) {
+ TLSKey value = TlsAlloc();
+ if (value != TLS_OUT_OF_INDEXES) {
+ *key = value;
+ return true;
}
-
- // Setup our destructor.
- g_tls_destructors[slot_] = destructor;
- initialized_ = true;
- return true;
+ return false;
}
-void ThreadLocalStorage::StaticSlot::Free() {
- // At this time, we don't reclaim old indices for TLS slots.
- // So all we need to do is wipe the destructor.
- DCHECK_GT(slot_, 0);
- DCHECK_LT(slot_, kThreadLocalStorageSize);
- g_tls_destructors[slot_] = NULL;
- slot_ = 0;
- initialized_ = false;
+void PlatformThreadLocalStorage::FreeTLS(TLSKey key) {
+ BOOL ret = TlsFree(key);
+ DCHECK(ret);
}
-void* ThreadLocalStorage::StaticSlot::Get() const {
- void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
- if (!tls_data)
- tls_data = ConstructTlsVector();
- DCHECK_GT(slot_, 0);
- DCHECK_LT(slot_, kThreadLocalStorageSize);
- return tls_data[slot_];
+void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) {
+ return TlsGetValue(key);
}
-void ThreadLocalStorage::StaticSlot::Set(void* value) {
- void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key));
- if (!tls_data)
- tls_data = ConstructTlsVector();
- DCHECK_GT(slot_, 0);
- DCHECK_LT(slot_, kThreadLocalStorageSize);
- tls_data[slot_] = value;
+void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) {
+ BOOL ret = TlsSetValue(key, value);
+ DCHECK(ret);
}
+} // namespace internal
+
} // namespace base
// Thread Termination Callbacks.
@@ -233,7 +67,7 @@ void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
// On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
// and on W2K and W2K3. So don't assume it is sent.
if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason)
- WinThreadExit();
+ base::internal::PlatformThreadLocalStorage::OnThreadExit();
}
// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
diff --git a/chromium/base/threading/thread_perftest.cc b/chromium/base/threading/thread_perftest.cc
new file mode 100644
index 00000000000..eaeddf94435
--- /dev/null
+++ b/chromium/base/threading/thread_perftest.cc
@@ -0,0 +1,314 @@
+// Copyright 2014 The Chromium Authors. All 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"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_vector.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/perf/perf_test.h"
+
+#if defined(OS_POSIX)
+#include <pthread.h>
+#endif
+
+namespace base {
+
+namespace {
+
+const int kNumRuns = 100000;
+
+// Base class for a threading perf-test. This sets up some threads for the
+// test and measures the clock-time in addition to time spent on each thread.
+class ThreadPerfTest : public testing::Test {
+ public:
+ ThreadPerfTest()
+ : done_(false, false) {
+ // Disable the task profiler as it adds significant cost!
+ CommandLine::Init(0, NULL);
+ CommandLine::ForCurrentProcess()->AppendSwitchASCII(
+ switches::kProfilerTiming,
+ switches::kProfilerTimingDisabledValue);
+ }
+
+ // To be implemented by each test. Subclass must uses threads_ such that
+ // their cpu-time can be measured. Test must return from PingPong() _and_
+ // call FinishMeasurement from any thread to complete the test.
+ virtual void Init() {}
+ virtual void PingPong(int hops) = 0;
+ virtual void Reset() {}
+
+ void TimeOnThread(base::TimeTicks* ticks, base::WaitableEvent* done) {
+ *ticks = base::TimeTicks::ThreadNow();
+ done->Signal();
+ }
+
+ base::TimeTicks ThreadNow(base::Thread* thread) {
+ base::WaitableEvent done(false, false);
+ base::TimeTicks ticks;
+ thread->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&ThreadPerfTest::TimeOnThread,
+ base::Unretained(this),
+ &ticks,
+ &done));
+ done.Wait();
+ return ticks;
+ }
+
+ void RunPingPongTest(const std::string& name, unsigned num_threads) {
+ // Create threads and collect starting cpu-time for each thread.
+ std::vector<base::TimeTicks> thread_starts;
+ while (threads_.size() < num_threads) {
+ threads_.push_back(new base::Thread("PingPonger"));
+ threads_.back()->Start();
+ if (base::TimeTicks::IsThreadNowSupported())
+ thread_starts.push_back(ThreadNow(threads_.back()));
+ }
+
+ Init();
+
+ base::TimeTicks start = base::TimeTicks::HighResNow();
+ PingPong(kNumRuns);
+ done_.Wait();
+ base::TimeTicks end = base::TimeTicks::HighResNow();
+
+ // Gather the cpu-time spent on each thread. This does one extra tasks,
+ // but that should be in the noise given enough runs.
+ base::TimeDelta thread_time;
+ while (threads_.size()) {
+ if (base::TimeTicks::IsThreadNowSupported()) {
+ thread_time += ThreadNow(threads_.back()) - thread_starts.back();
+ thread_starts.pop_back();
+ }
+ threads_.pop_back();
+ }
+
+ Reset();
+
+ double num_runs = static_cast<double>(kNumRuns);
+ double us_per_task_clock = (end - start).InMicroseconds() / num_runs;
+ double us_per_task_cpu = thread_time.InMicroseconds() / num_runs;
+
+ // Clock time per task.
+ perf_test::PrintResult(
+ "task", "", name + "_time ", us_per_task_clock, "us/hop", true);
+
+ // Total utilization across threads if available (likely higher).
+ if (base::TimeTicks::IsThreadNowSupported()) {
+ perf_test::PrintResult(
+ "task", "", name + "_cpu ", us_per_task_cpu, "us/hop", true);
+ }
+ }
+
+ protected:
+ void FinishMeasurement() { done_.Signal(); }
+ ScopedVector<base::Thread> threads_;
+
+ private:
+ base::WaitableEvent done_;
+};
+
+// Class to test task performance by posting empty tasks back and forth.
+class TaskPerfTest : public ThreadPerfTest {
+ base::Thread* NextThread(int count) {
+ return threads_[count % threads_.size()];
+ }
+
+ virtual void PingPong(int hops) OVERRIDE {
+ if (!hops) {
+ FinishMeasurement();
+ return;
+ }
+ NextThread(hops)->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(
+ &ThreadPerfTest::PingPong, base::Unretained(this), hops - 1));
+ }
+};
+
+// This tries to test the 'best-case' as well as the 'worst-case' task posting
+// performance. The best-case keeps one thread alive such that it never yeilds,
+// while the worse-case forces a context switch for every task. Four threads are
+// used to ensure the threads do yeild (with just two it might be possible for
+// both threads to stay awake if they can signal each other fast enough).
+TEST_F(TaskPerfTest, TaskPingPong) {
+ RunPingPongTest("1_Task_Threads", 1);
+ RunPingPongTest("4_Task_Threads", 4);
+}
+
+
+// Same as above, but add observers to test their perf impact.
+class MessageLoopObserver : public base::MessageLoop::TaskObserver {
+ public:
+ virtual void WillProcessTask(const base::PendingTask& pending_task) OVERRIDE {
+ }
+ virtual void DidProcessTask(const base::PendingTask& pending_task) OVERRIDE {
+ }
+};
+MessageLoopObserver message_loop_observer;
+
+class TaskObserverPerfTest : public TaskPerfTest {
+ public:
+ virtual void Init() OVERRIDE {
+ TaskPerfTest::Init();
+ for (size_t i = 0; i < threads_.size(); i++) {
+ threads_[i]->message_loop()->AddTaskObserver(&message_loop_observer);
+ }
+ }
+};
+
+TEST_F(TaskObserverPerfTest, TaskPingPong) {
+ RunPingPongTest("1_Task_Threads_With_Observer", 1);
+ RunPingPongTest("4_Task_Threads_With_Observer", 4);
+}
+
+// Class to test our WaitableEvent performance by signaling back and fort.
+// WaitableEvent is templated so we can also compare with other versions.
+template <typename WaitableEventType>
+class EventPerfTest : public ThreadPerfTest {
+ public:
+ virtual void Init() OVERRIDE {
+ for (size_t i = 0; i < threads_.size(); i++)
+ events_.push_back(new WaitableEventType(false, false));
+ }
+
+ virtual void Reset() OVERRIDE { events_.clear(); }
+
+ void WaitAndSignalOnThread(size_t event) {
+ size_t next_event = (event + 1) % events_.size();
+ int my_hops = 0;
+ do {
+ events_[event]->Wait();
+ my_hops = --remaining_hops_; // We own 'hops' between Wait and Signal.
+ events_[next_event]->Signal();
+ } while (my_hops > 0);
+ // Once we are done, all threads will signal as hops passes zero.
+ // We only signal completion once, on the thread that reaches zero.
+ if (!my_hops)
+ FinishMeasurement();
+ }
+
+ virtual void PingPong(int hops) OVERRIDE {
+ remaining_hops_ = hops;
+ for (size_t i = 0; i < threads_.size(); i++) {
+ threads_[i]->message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&EventPerfTest::WaitAndSignalOnThread,
+ base::Unretained(this),
+ i));
+ }
+
+ // Kick off the Signal ping-ponging.
+ events_.front()->Signal();
+ }
+
+ int remaining_hops_;
+ ScopedVector<WaitableEventType> events_;
+};
+
+// Similar to the task posting test, this just tests similar functionality
+// using WaitableEvents. We only test four threads (worst-case), but we
+// might want to craft a way to test the best-case (where the thread doesn't
+// end up blocking because the event is already signalled).
+typedef EventPerfTest<base::WaitableEvent> WaitableEventPerfTest;
+TEST_F(WaitableEventPerfTest, EventPingPong) {
+ RunPingPongTest("4_WaitableEvent_Threads", 4);
+}
+
+// Build a minimal event using ConditionVariable.
+class ConditionVariableEvent {
+ public:
+ ConditionVariableEvent(bool manual_reset, bool initially_signaled)
+ : cond_(&lock_), signaled_(false) {
+ DCHECK(!manual_reset);
+ DCHECK(!initially_signaled);
+ }
+
+ void Signal() {
+ {
+ base::AutoLock scoped_lock(lock_);
+ signaled_ = true;
+ }
+ cond_.Signal();
+ }
+
+ void Wait() {
+ base::AutoLock scoped_lock(lock_);
+ while (!signaled_)
+ cond_.Wait();
+ signaled_ = false;
+ }
+
+ private:
+ base::Lock lock_;
+ base::ConditionVariable cond_;
+ bool signaled_;
+};
+
+// This is meant to test the absolute minimal context switching time
+// using our own base synchronization code.
+typedef EventPerfTest<ConditionVariableEvent> ConditionVariablePerfTest;
+TEST_F(ConditionVariablePerfTest, EventPingPong) {
+ RunPingPongTest("4_ConditionVariable_Threads", 4);
+}
+
+#if defined(OS_POSIX)
+
+// Absolutely 100% minimal posix waitable event. If there is a better/faster
+// way to force a context switch, we should use that instead.
+class PthreadEvent {
+ public:
+ PthreadEvent(bool manual_reset, bool initially_signaled) {
+ DCHECK(!manual_reset);
+ DCHECK(!initially_signaled);
+ pthread_mutex_init(&mutex_, 0);
+ pthread_cond_init(&cond_, 0);
+ signaled_ = false;
+ }
+
+ ~PthreadEvent() {
+ pthread_cond_destroy(&cond_);
+ pthread_mutex_destroy(&mutex_);
+ }
+
+ void Signal() {
+ pthread_mutex_lock(&mutex_);
+ signaled_ = true;
+ pthread_mutex_unlock(&mutex_);
+ pthread_cond_signal(&cond_);
+ }
+
+ void Wait() {
+ pthread_mutex_lock(&mutex_);
+ while (!signaled_)
+ pthread_cond_wait(&cond_, &mutex_);
+ signaled_ = false;
+ pthread_mutex_unlock(&mutex_);
+ }
+
+ private:
+ bool signaled_;
+ pthread_mutex_t mutex_;
+ pthread_cond_t cond_;
+};
+
+// This is meant to test the absolute minimal context switching time.
+// If there is any faster way to do this we should substitute it in.
+typedef EventPerfTest<PthreadEvent> PthreadEventPerfTest;
+TEST_F(PthreadEventPerfTest, EventPingPong) {
+ RunPingPongTest("4_PthreadCondVar_Threads", 4);
+}
+
+#endif
+
+} // namespace
+
+} // namespace base
diff --git a/chromium/base/threading/worker_pool_posix.cc b/chromium/base/threading/worker_pool_posix.cc
index c4c523f270d..f00d7994d01 100644
--- a/chromium/base/threading/worker_pool_posix.cc
+++ b/chromium/base/threading/worker_pool_posix.cc
@@ -91,7 +91,7 @@ void WorkerThread::ThreadMain() {
PendingTask pending_task = pool_->WaitForTask();
if (pending_task.task.is_null())
break;
- TRACE_EVENT2("task", "WorkerThread::ThreadMain::Run",
+ TRACE_EVENT2("toplevel", "WorkerThread::ThreadMain::Run",
"src_file", pending_task.posted_from.file_name(),
"src_func", pending_task.posted_from.function_name());
diff --git a/chromium/base/threading/worker_pool_win.cc b/chromium/base/threading/worker_pool_win.cc
index c27010fb12f..fa11dc5a763 100644
--- a/chromium/base/threading/worker_pool_win.cc
+++ b/chromium/base/threading/worker_pool_win.cc
@@ -21,7 +21,7 @@ base::LazyInstance<ThreadLocalBoolean>::Leaky
DWORD CALLBACK WorkItemCallback(void* param) {
PendingTask* pending_task = static_cast<PendingTask*>(param);
- TRACE_EVENT2("task", "WorkItemCallback::Run",
+ TRACE_EVENT2("toplevel", "WorkItemCallback::Run",
"src_file", pending_task->posted_from.file_name(),
"src_func", pending_task->posted_from.function_name());
@@ -48,7 +48,7 @@ bool PostTaskInternal(PendingTask* pending_task, bool task_is_slow) {
flags |= WT_EXECUTELONGFUNCTION;
if (!QueueUserWorkItem(WorkItemCallback, pending_task, flags)) {
- DLOG_GETLASTERROR(ERROR) << "QueueUserWorkItem failed";
+ DPLOG(ERROR) << "QueueUserWorkItem failed";
delete pending_task;
return false;
}
diff --git a/chromium/base/time/pr_time_unittest.cc b/chromium/base/time/pr_time_unittest.cc
index 9e0c4ed04f0..105b8e45318 100644
--- a/chromium/base/time/pr_time_unittest.cc
+++ b/chromium/base/time/pr_time_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <stdint.h>
#include <time.h>
#include "base/compiler_specific.h"
@@ -16,6 +17,10 @@ namespace {
// time_t representation of 15th Oct 2007 12:45:00 PDT
PRTime comparison_time_pdt = 1192477500 * Time::kMicrosecondsPerSecond;
+// Time with positive tz offset and fractional seconds:
+// 2013-07-08T11:28:12.441381+02:00
+PRTime comparison_time_2 = INT64_C(1373275692441381); // represented as GMT
+
// Specialized test fixture allowing time strings without timezones to be
// tested by comparing them to a known time in the local zone.
class PRTimeTest : public testing::Test {
@@ -36,12 +41,30 @@ class PRTimeTest : public testing::Test {
0, // day of year (ignored, output only)
-1 // DST in effect, -1 tells mktime to figure it out
};
- comparison_time_local_ = mktime(&local_comparison_tm) *
- Time::kMicrosecondsPerSecond;
+ comparison_time_local_ =
+ mktime(&local_comparison_tm) * Time::kMicrosecondsPerSecond;
ASSERT_GT(comparison_time_local_, 0);
+
+ const int microseconds = 441381;
+ struct tm local_comparison_tm_2 = {
+ 12, // second
+ 28, // minute
+ 11, // hour
+ 8, // day of month
+ 7 - 1, // month
+ 2013 - 1900, // year
+ 0, // day of week (ignored, output only)
+ 0, // day of year (ignored, output only)
+ -1 // DST in effect, -1 tells mktime to figure it out
+ };
+ comparison_time_local_2_ =
+ mktime(&local_comparison_tm_2) * Time::kMicrosecondsPerSecond;
+ ASSERT_GT(comparison_time_local_2_, 0);
+ comparison_time_local_2_ += microseconds;
}
PRTime comparison_time_local_;
+ PRTime comparison_time_local_2_;
};
// Tests the PR_ParseTimeString nspr helper function for
@@ -74,7 +97,7 @@ TEST_F(PRTimeTest, ParseTimeTest2) {
PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT",
PR_FALSE, &parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_pdt);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest3) {
@@ -82,7 +105,7 @@ TEST_F(PRTimeTest, ParseTimeTest3) {
PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE,
&parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_local_);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest4) {
@@ -90,7 +113,7 @@ TEST_F(PRTimeTest, ParseTimeTest4) {
PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE,
&parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_pdt);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest5) {
@@ -98,7 +121,7 @@ TEST_F(PRTimeTest, ParseTimeTest5) {
PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007",
PR_FALSE, &parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_pdt);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest6) {
@@ -106,7 +129,7 @@ TEST_F(PRTimeTest, ParseTimeTest6) {
PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM",
PR_FALSE, &parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_local_);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest7) {
@@ -114,23 +137,123 @@ TEST_F(PRTimeTest, ParseTimeTest7) {
PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE,
&parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_local_);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest8) {
PRTime parsed_time = 0;
- PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE,
+ PRStatus result = PR_ParseTimeString("10/15/07 12:45:00. PM", PR_FALSE,
&parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_local_);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
}
TEST_F(PRTimeTest, ParseTimeTest9) {
PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("10/15/07 12:45:00.0 PM", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest10) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_local_, parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest11) {
+ PRTime parsed_time = 0;
PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)",
PR_FALSE, &parsed_time);
EXPECT_EQ(PR_SUCCESS, result);
- EXPECT_EQ(parsed_time, comparison_time_pdt);
+ EXPECT_EQ(comparison_time_pdt, parsed_time);
+}
+
+// hh:mm timezone offset.
+TEST_F(PRTimeTest, ParseTimeTest12) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381+02:00",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2, parsed_time);
+}
+
+// hhmm timezone offset.
+TEST_F(PRTimeTest, ParseTimeTest13) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381+0200",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2, parsed_time);
+}
+
+// hh timezone offset.
+TEST_F(PRTimeTest, ParseTimeTest14) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.4413819+02",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2, parsed_time);
+}
+
+// 5 digits fractional second.
+TEST_F(PRTimeTest, ParseTimeTest15) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T09:28:12.44138Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2-1, parsed_time);
+}
+
+// Fractional seconds, local timezone.
+TEST_F(PRTimeTest, ParseTimeTest16) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T11:28:12.441381",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_local_2_, parsed_time);
+}
+
+// "Z" (=GMT) timezone.
+TEST_F(PRTimeTest, ParseTimeTest17) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08T09:28:12.441381Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2, parsed_time);
+}
+
+// "T" delimiter replaced by space.
+TEST_F(PRTimeTest, ParseTimeTest18) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-08 09:28:12.441381Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(comparison_time_2, parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTestInvalid1) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("201-07-08T09:28:12.441381Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_FAILURE, result);
+}
+
+TEST_F(PRTimeTest, ParseTimeTestInvalid2) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-007-08T09:28:12.441381Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_FAILURE, result);
+}
+
+TEST_F(PRTimeTest, ParseTimeTestInvalid3) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("2013-07-008T09:28:12.441381Z",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_FAILURE, result);
}
// This test should not crash when compiled with Visual C++ 2005 (see
diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc
index 9f3c53d6695..f7313565a3e 100644
--- a/chromium/base/time/time.cc
+++ b/chromium/base/time/time.cc
@@ -11,46 +11,86 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/third_party/nspr/prtime.h"
-#include "base/third_party/nspr/prtypes.h"
namespace base {
// TimeDelta ------------------------------------------------------------------
+// static
+TimeDelta TimeDelta::Max() {
+ return TimeDelta(std::numeric_limits<int64>::max());
+}
+
int TimeDelta::InDays() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
}
int TimeDelta::InHours() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
}
int TimeDelta::InMinutes() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int>::max();
+ }
return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
}
double TimeDelta::InSecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
}
int64 TimeDelta::InSeconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
return delta_ / Time::kMicrosecondsPerSecond;
}
double TimeDelta::InMillisecondsF() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<double>::infinity();
+ }
return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
}
int64 TimeDelta::InMilliseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
return delta_ / Time::kMicrosecondsPerMillisecond;
}
int64 TimeDelta::InMillisecondsRoundedUp() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
return (delta_ + Time::kMicrosecondsPerMillisecond - 1) /
Time::kMicrosecondsPerMillisecond;
}
int64 TimeDelta::InMicroseconds() const {
+ if (is_max()) {
+ // Preserve max to prevent overflow.
+ return std::numeric_limits<int64>::max();
+ }
return delta_;
}
@@ -89,7 +129,7 @@ time_t Time::ToTimeT() const {
Time Time::FromDoubleT(double dt) {
if (dt == 0 || IsNaN(dt))
return Time(); // Preserve 0 so we can tell it doesn't exist.
- if (dt == std::numeric_limits<double>::max())
+ if (dt == std::numeric_limits<double>::infinity())
return Max();
return Time(static_cast<int64>((dt *
static_cast<double>(kMicrosecondsPerSecond)) +
@@ -101,7 +141,7 @@ double Time::ToDoubleT() const {
return 0; // Preserve 0 so we can tell it doesn't exist.
if (is_max()) {
// Preserve max without offset to prevent overflow.
- return std::numeric_limits<double>::max();
+ return std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
static_cast<double>(kMicrosecondsPerSecond));
@@ -120,7 +160,7 @@ Time Time::FromTimeSpec(const timespec& ts) {
Time Time::FromJsTime(double ms_since_epoch) {
// The epoch is a valid time, so this constructor doesn't interpret
// 0 as the null time.
- if (ms_since_epoch == std::numeric_limits<double>::max())
+ if (ms_since_epoch == std::numeric_limits<double>::infinity())
return Max();
return Time(static_cast<int64>(ms_since_epoch * kMicrosecondsPerMillisecond) +
kTimeTToMicrosecondsOffset);
@@ -133,7 +173,7 @@ double Time::ToJsTime() const {
}
if (is_max()) {
// Preserve max without offset to prevent overflow.
- return std::numeric_limits<double>::max();
+ return std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
kMicrosecondsPerMillisecond);
diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h
index 40566b92317..41d662afa6a 100644
--- a/chromium/base/time/time.h
+++ b/chromium/base/time/time.h
@@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-// Time represents an absolute point in time, internally represented as
-// microseconds (s/1,000,000) since the Windows epoch (1601-01-01 00:00:00 UTC)
-// (See http://crbug.com/14734). System-dependent clock interface routines are
-// defined in time_PLATFORM.cc.
+// Time represents an absolute point in coordinated universal time (UTC),
+// internally represented as microseconds (s/1,000,000) since the Windows epoch
+// (1601-01-01 00:00:00 UTC) (See http://crbug.com/14734). System-dependent
+// clock interface routines are defined in time_PLATFORM.cc.
//
// TimeDelta represents a duration of time, internally represented in
// microseconds.
@@ -61,11 +61,13 @@ class BASE_EXPORT TimeDelta {
}
// Converts units of time to TimeDeltas.
- static TimeDelta FromDays(int64 days);
- static TimeDelta FromHours(int64 hours);
- static TimeDelta FromMinutes(int64 minutes);
+ static TimeDelta FromDays(int days);
+ static TimeDelta FromHours(int hours);
+ static TimeDelta FromMinutes(int minutes);
static TimeDelta FromSeconds(int64 secs);
static TimeDelta FromMilliseconds(int64 ms);
+ static TimeDelta FromSecondsD(double secs);
+ static TimeDelta FromMillisecondsD(double ms);
static TimeDelta FromMicroseconds(int64 us);
#if defined(OS_WIN)
static TimeDelta FromQPCValue(LONGLONG qpc_value);
@@ -79,6 +81,11 @@ class BASE_EXPORT TimeDelta {
return TimeDelta(delta);
}
+ // Returns the maximum time delta, which should be greater than any reasonable
+ // time delta we might compare it to. Adding or subtracting the maximum time
+ // delta to a time or another time delta has an undefined result.
+ static TimeDelta Max();
+
// Returns the internal numeric value of the TimeDelta object. Please don't
// use this and do arithmetic on it, as it is more error prone than using the
// provided operators.
@@ -87,6 +94,11 @@ class BASE_EXPORT TimeDelta {
return delta_;
}
+ // Returns true if the time delta is the maximum time delta.
+ bool is_max() const {
+ return delta_ == std::numeric_limits<int64>::max();
+ }
+
#if defined(OS_POSIX)
struct timespec ToTimeSpec() const;
#endif
@@ -196,7 +208,7 @@ inline TimeDelta operator*(int64 a, TimeDelta td) {
// Time -----------------------------------------------------------------------
-// Represents a wall clock time.
+// Represents a wall clock time in UTC.
class BASE_EXPORT Time {
public:
static const int64 kMillisecondsPerSecond = 1000;
@@ -493,32 +505,66 @@ class BASE_EXPORT Time {
// Inline the TimeDelta factory methods, for fast TimeDelta construction.
// static
-inline TimeDelta TimeDelta::FromDays(int64 days) {
+inline TimeDelta TimeDelta::FromDays(int days) {
+ // Preserve max to prevent overflow.
+ if (days == std::numeric_limits<int>::max())
+ return Max();
return TimeDelta(days * Time::kMicrosecondsPerDay);
}
// static
-inline TimeDelta TimeDelta::FromHours(int64 hours) {
+inline TimeDelta TimeDelta::FromHours(int hours) {
+ // Preserve max to prevent overflow.
+ if (hours == std::numeric_limits<int>::max())
+ return Max();
return TimeDelta(hours * Time::kMicrosecondsPerHour);
}
// static
-inline TimeDelta TimeDelta::FromMinutes(int64 minutes) {
+inline TimeDelta TimeDelta::FromMinutes(int minutes) {
+ // Preserve max to prevent overflow.
+ if (minutes == std::numeric_limits<int>::max())
+ return Max();
return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
}
// static
inline TimeDelta TimeDelta::FromSeconds(int64 secs) {
+ // Preserve max to prevent overflow.
+ if (secs == std::numeric_limits<int64>::max())
+ return Max();
return TimeDelta(secs * Time::kMicrosecondsPerSecond);
}
// static
inline TimeDelta TimeDelta::FromMilliseconds(int64 ms) {
+ // Preserve max to prevent overflow.
+ if (ms == std::numeric_limits<int64>::max())
+ return Max();
+ return TimeDelta(ms * Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+inline TimeDelta TimeDelta::FromSecondsD(double secs) {
+ // Preserve max to prevent overflow.
+ if (secs == std::numeric_limits<double>::infinity())
+ return Max();
+ return TimeDelta(secs * Time::kMicrosecondsPerSecond);
+}
+
+// static
+inline TimeDelta TimeDelta::FromMillisecondsD(double ms) {
+ // Preserve max to prevent overflow.
+ if (ms == std::numeric_limits<double>::infinity())
+ return Max();
return TimeDelta(ms * Time::kMicrosecondsPerMillisecond);
}
// static
inline TimeDelta TimeDelta::FromMicroseconds(int64 us) {
+ // Preserve max to prevent overflow.
+ if (us == std::numeric_limits<int64>::max())
+ return Max();
return TimeDelta(us);
}
@@ -530,6 +576,14 @@ inline Time TimeDelta::operator+(Time t) const {
class BASE_EXPORT TimeTicks {
public:
+ // We define this even without OS_CHROMEOS for seccomp sandbox testing.
+#if defined(OS_LINUX)
+ // Force definition of the system trace clock; it is a chromeos-only api
+ // at the moment and surfacing it in the right place requires mucking
+ // with glibc et al.
+ static const clockid_t kClockSystemTrace = 11;
+#endif
+
TimeTicks() : ticks_(0) {
}
diff --git a/chromium/base/time/time_mac.cc b/chromium/base/time/time_mac.cc
index 2388ddc6e69..26c5d7a715b 100644
--- a/chromium/base/time/time_mac.cc
+++ b/chromium/base/time/time_mac.cc
@@ -15,6 +15,7 @@
#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/mac/mach_logging.h"
#include "base/mac/scoped_cftyperef.h"
#include "base/mac/scoped_mach_port.h"
@@ -46,7 +47,7 @@ uint64_t ComputeCurrentTicks() {
// whether mach_timebase_info has already been called. This is
// recommended by Apple's QA1398.
kern_return_t kr = mach_timebase_info(&timebase_info);
- DCHECK_EQ(KERN_SUCCESS, kr);
+ MACH_DCHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info";
}
// mach_absolute_time is it when it comes to ticks on the Mac. Other calls
@@ -71,11 +72,11 @@ uint64_t ComputeThreadTicks() {
NOTREACHED();
return 0;
#else
- base::mac::ScopedMachPort thread(mach_thread_self());
+ base::mac::ScopedMachSendRight thread(mach_thread_self());
mach_msg_type_number_t thread_info_count = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t thread_info_data;
- if (thread == MACH_PORT_NULL) {
+ if (thread.get() == MACH_PORT_NULL) {
DLOG(ERROR) << "Failed to get mach_thread_self()";
return 0;
}
@@ -85,7 +86,7 @@ uint64_t ComputeThreadTicks() {
THREAD_BASIC_INFO,
reinterpret_cast<thread_info_t>(&thread_info_data),
&thread_info_count);
- DCHECK_EQ(KERN_SUCCESS, kr);
+ MACH_DCHECK(kr == KERN_SUCCESS, kr) << "thread_info";
return (thread_info_data.user_time.seconds *
base::Time::kMicrosecondsPerSecond) +
@@ -131,9 +132,11 @@ Time Time::Now() {
// static
Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) {
+ COMPILE_ASSERT(std::numeric_limits<CFAbsoluteTime>::has_infinity,
+ numeric_limits_infinity_is_undefined_when_not_has_infinity);
if (t == 0)
return Time(); // Consider 0 as a null Time.
- if (t == std::numeric_limits<CFAbsoluteTime>::max())
+ if (t == std::numeric_limits<CFAbsoluteTime>::infinity())
return Max();
return Time(static_cast<int64>(
(t + kCFAbsoluteTimeIntervalSince1970) * kMicrosecondsPerSecond) +
@@ -141,10 +144,12 @@ Time Time::FromCFAbsoluteTime(CFAbsoluteTime t) {
}
CFAbsoluteTime Time::ToCFAbsoluteTime() const {
+ COMPILE_ASSERT(std::numeric_limits<CFAbsoluteTime>::has_infinity,
+ numeric_limits_infinity_is_undefined_when_not_has_infinity);
if (is_null())
return 0; // Consider 0 as a null Time.
if (is_max())
- return std::numeric_limits<CFAbsoluteTime>::max();
+ return std::numeric_limits<CFAbsoluteTime>::infinity();
return (static_cast<CFAbsoluteTime>(us_ - kWindowsEpochDeltaMicroseconds) /
kMicrosecondsPerSecond) - kCFAbsoluteTimeIntervalSince1970;
}
diff --git a/chromium/base/time/time_posix.cc b/chromium/base/time/time_posix.cc
index 3378038fe44..b5b8213846d 100644
--- a/chromium/base/time/time_posix.cc
+++ b/chromium/base/time/time_posix.cc
@@ -7,7 +7,7 @@
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && !defined(__LP64__)
#include <time64.h>
#endif
#include <unistd.h>
@@ -32,7 +32,7 @@ namespace {
// Define a system-specific SysTime that wraps either to a time_t or
// a time64_t depending on the host system, and associated convertion.
// See crbug.com/162007
-#if defined(OS_ANDROID)
+#if defined(OS_ANDROID) && !defined(__LP64__)
typedef time64_t SysTime;
SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
@@ -49,7 +49,7 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
gmtime64_r(&t, timestruct);
}
-#else // OS_ANDROID
+#else // OS_ANDROID && !__LP64__
typedef time_t SysTime;
SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) {
@@ -140,7 +140,7 @@ Time Time::Now() {
struct timezone tz = { 0, 0 }; // UTC
if (gettimeofday(&tv, &tz) != 0) {
DCHECK(0) << "Could not determine time of day";
- LOG_ERRNO(ERROR) << "Call to gettimeofday failed.";
+ PLOG(ERROR) << "Call to gettimeofday failed.";
// Return null instead of uninitialized |tv| value, which contains random
// garbage data. This may result in the crash seen in crbug.com/147570.
return Time();
@@ -320,18 +320,14 @@ TimeTicks TimeTicks::ThreadNow() {
#endif
}
+// Use the Chrome OS specific system-wide clock.
#if defined(OS_CHROMEOS)
-// Force definition of the system trace clock; it is a chromeos-only api
-// at the moment and surfacing it in the right place requires mucking
-// with glibc et al.
-#define CLOCK_SYSTEM_TRACE 11
-
// static
TimeTicks TimeTicks::NowFromSystemTraceTime() {
uint64_t absolute_micro;
struct timespec ts;
- if (clock_gettime(CLOCK_SYSTEM_TRACE, &ts) != 0) {
+ if (clock_gettime(kClockSystemTrace, &ts) != 0) {
// NB: fall-back for a chrome os build running on linux
return HighResNow();
}
@@ -343,14 +339,14 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() {
return TimeTicks(absolute_micro);
}
-#else // !defined(OS_CHROMEOS)
+#else // !defined(OS_CHROMEOS)
// static
TimeTicks TimeTicks::NowFromSystemTraceTime() {
return HighResNow();
}
-#endif // defined(OS_CHROMEOS)
+#endif // defined(OS_CHROMEOS)
#endif // !OS_MACOSX
diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc
index e78a61cf4bd..63c3a1ac771 100644
--- a/chromium/base/time/time_unittest.cc
+++ b/chromium/base/time/time_unittest.cc
@@ -481,6 +481,52 @@ TEST_F(TimeTest, ExplodeBeforeUnixEpoch) {
EXPECT_EQ(1, exploded.millisecond);
}
+TEST_F(TimeTest, TimeDeltaMax) {
+ TimeDelta max = TimeDelta::Max();
+ EXPECT_TRUE(max.is_max());
+ EXPECT_EQ(max, TimeDelta::Max());
+ EXPECT_GT(max, TimeDelta::FromDays(100 * 365));
+ EXPECT_GT(max, TimeDelta());
+}
+
+TEST_F(TimeTest, TimeDeltaMaxConversions) {
+ TimeDelta t = TimeDelta::Max();
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue());
+
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InDays());
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InHours());
+ EXPECT_EQ(std::numeric_limits<int>::max(), t.InMinutes());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InSecondsF());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InSeconds());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.InMillisecondsF());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMilliseconds());
+ EXPECT_EQ(std::numeric_limits<int64>::max(), t.InMillisecondsRoundedUp());
+
+ t = TimeDelta::FromDays(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromHours(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMinutes(std::numeric_limits<int>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromSeconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMilliseconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromSecondsD(std::numeric_limits<double>::infinity());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMillisecondsD(std::numeric_limits<double>::infinity());
+ EXPECT_TRUE(t.is_max());
+
+ t = TimeDelta::FromMicroseconds(std::numeric_limits<int64>::max());
+ EXPECT_TRUE(t.is_max());
+}
+
TEST_F(TimeTest, Max) {
Time max = Time::Max();
EXPECT_TRUE(max.is_max());
@@ -493,13 +539,13 @@ TEST_F(TimeTest, MaxConversions) {
Time t = Time::Max();
EXPECT_EQ(std::numeric_limits<int64>::max(), t.ToInternalValue());
- t = Time::FromDoubleT(std::numeric_limits<double>::max());
+ t = Time::FromDoubleT(std::numeric_limits<double>::infinity());
EXPECT_TRUE(t.is_max());
- EXPECT_EQ(std::numeric_limits<double>::max(), t.ToDoubleT());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToDoubleT());
- t = Time::FromJsTime(std::numeric_limits<double>::max());
+ t = Time::FromJsTime(std::numeric_limits<double>::infinity());
EXPECT_TRUE(t.is_max());
- EXPECT_EQ(std::numeric_limits<double>::max(), t.ToJsTime());
+ EXPECT_EQ(std::numeric_limits<double>::infinity(), t.ToJsTime());
t = Time::FromTimeT(std::numeric_limits<time_t>::max());
EXPECT_TRUE(t.is_max());
@@ -518,9 +564,10 @@ TEST_F(TimeTest, MaxConversions) {
#endif
#if defined(OS_MACOSX)
- t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::max());
+ t = Time::FromCFAbsoluteTime(std::numeric_limits<CFAbsoluteTime>::infinity());
EXPECT_TRUE(t.is_max());
- EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::max(), t.ToCFAbsoluteTime());
+ EXPECT_EQ(std::numeric_limits<CFAbsoluteTime>::infinity(),
+ t.ToCFAbsoluteTime());
#endif
#if defined(OS_WIN)
@@ -635,7 +682,14 @@ TEST(TimeTicks, HighResNow) {
HighResClockTest(&TimeTicks::HighResNow);
}
-TEST(TimeTicks, ThreadNow) {
+// Fails frequently on Android http://crbug.com/352633 with:
+// Expected: (delta_thread.InMicroseconds()) > (0), actual: 0 vs 0
+#if defined(OS_ANDROID)
+#define MAYBE_ThreadNow DISABLED_ThreadNow
+#else
+#define MAYBE_ThreadNow ThreadNow
+#endif
+TEST(TimeTicks, MAYBE_ThreadNow) {
if (TimeTicks::IsThreadNowSupported()) {
TimeTicks begin = TimeTicks::Now();
TimeTicks begin_thread = TimeTicks::ThreadNow();
@@ -667,6 +721,10 @@ TEST(TimeDelta, FromAndIn) {
EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000));
EXPECT_TRUE(TimeDelta::FromMilliseconds(2) ==
TimeDelta::FromMicroseconds(2000));
+ EXPECT_TRUE(TimeDelta::FromSecondsD(2.3) ==
+ TimeDelta::FromMilliseconds(2300));
+ EXPECT_TRUE(TimeDelta::FromMillisecondsD(2.5) ==
+ TimeDelta::FromMicroseconds(2500));
EXPECT_EQ(13, TimeDelta::FromDays(13).InDays());
EXPECT_EQ(13, TimeDelta::FromHours(13).InHours());
EXPECT_EQ(13, TimeDelta::FromMinutes(13).InMinutes());
@@ -674,6 +732,10 @@ TEST(TimeDelta, FromAndIn) {
EXPECT_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF());
EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).InMilliseconds());
EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF());
+ EXPECT_EQ(13, TimeDelta::FromSecondsD(13.1).InSeconds());
+ EXPECT_EQ(13.1, TimeDelta::FromSecondsD(13.1).InSecondsF());
+ EXPECT_EQ(13, TimeDelta::FromMillisecondsD(13.3).InMilliseconds());
+ EXPECT_EQ(13.3, TimeDelta::FromMillisecondsD(13.3).InMillisecondsF());
EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds());
}
diff --git a/chromium/base/time/time_win.cc b/chromium/base/time/time_win.cc
index ef8d29a92cb..93f0c25e2c4 100644
--- a/chromium/base/time/time_win.cc
+++ b/chromium/base/time/time_win.cc
@@ -42,8 +42,8 @@
#include "base/basictypes.h"
#include "base/cpu.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
using base::Time;
@@ -358,8 +358,14 @@ bool IsBuggyAthlon(const base::CPU& cpu) {
// retrieve and more reliable.
class HighResNowSingleton {
public:
- static HighResNowSingleton* GetInstance() {
- return Singleton<HighResNowSingleton>::get();
+ HighResNowSingleton()
+ : ticks_per_second_(0),
+ skew_(0) {
+ InitializeClock();
+
+ base::CPU cpu;
+ if (IsBuggyAthlon(cpu))
+ DisableHighResClock();
}
bool IsUsingHighResClock() {
@@ -399,16 +405,6 @@ class HighResNowSingleton {
}
private:
- HighResNowSingleton()
- : ticks_per_second_(0),
- skew_(0) {
- InitializeClock();
-
- base::CPU cpu;
- if (IsBuggyAthlon(cpu))
- DisableHighResClock();
- }
-
// Synchronize the QPC clock with GetSystemTimeAsFileTime.
void InitializeClock() {
LARGE_INTEGER ticks_per_sec = {0};
@@ -433,12 +429,17 @@ class HighResNowSingleton {
int64 ticks_per_second_; // 0 indicates QPF failed and we're broken.
int64 skew_; // Skew between lo-res and hi-res clocks (for debugging).
-
- friend struct DefaultSingletonTraits<HighResNowSingleton>;
};
+static base::LazyInstance<HighResNowSingleton>::Leaky
+ leaky_high_res_now_singleton = LAZY_INSTANCE_INITIALIZER;
+
+HighResNowSingleton* GetHighResNowSingleton() {
+ return leaky_high_res_now_singleton.Pointer();
+}
+
TimeDelta HighResNowWrapper() {
- return HighResNowSingleton::GetInstance()->Now();
+ return GetHighResNowSingleton()->Now();
}
typedef TimeDelta (*NowFunction)(void);
@@ -485,7 +486,7 @@ TimeTicks TimeTicks::Now() {
// static
TimeTicks TimeTicks::HighResNow() {
- return TimeTicks() + HighResNowSingleton::GetInstance()->Now();
+ return TimeTicks() + HighResNowWrapper();
}
// static
@@ -506,18 +507,17 @@ TimeTicks TimeTicks::NowFromSystemTraceTime() {
// static
int64 TimeTicks::GetQPCDriftMicroseconds() {
- return HighResNowSingleton::GetInstance()->GetQPCDriftMicroseconds();
+ return GetHighResNowSingleton()->GetQPCDriftMicroseconds();
}
// static
TimeTicks TimeTicks::FromQPCValue(LONGLONG qpc_value) {
- return TimeTicks(
- HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value));
+ return TimeTicks(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value));
}
// static
bool TimeTicks::IsHighResClockWorking() {
- return HighResNowSingleton::GetInstance()->IsUsingHighResClock();
+ return GetHighResNowSingleton()->IsUsingHighResClock();
}
TimeTicks TimeTicks::UnprotectedNow() {
@@ -532,6 +532,5 @@ TimeTicks TimeTicks::UnprotectedNow() {
// static
TimeDelta TimeDelta::FromQPCValue(LONGLONG qpc_value) {
- return TimeDelta(
- HighResNowSingleton::GetInstance()->QPCValueToMicroseconds(qpc_value));
+ return TimeDelta(GetHighResNowSingleton()->QPCValueToMicroseconds(qpc_value));
}
diff --git a/chromium/base/time/time_win_unittest.cc b/chromium/base/time/time_win_unittest.cc
index 803222febcb..9c7654fde45 100644
--- a/chromium/base/time/time_win_unittest.cc
+++ b/chromium/base/time/time_win_unittest.cc
@@ -99,6 +99,9 @@ TEST(TimeTicks, WinRollover) {
for (int index = 0; index < kThreads; index++) {
DWORD rv = WaitForSingleObject(threads[index], INFINITE);
EXPECT_EQ(rv, WAIT_OBJECT_0);
+ // Since using _beginthreadex() (as opposed to _beginthread),
+ // an explicit CloseHandle() is supposed to be called.
+ CloseHandle(threads[index]);
}
CloseHandle(g_rollover_test_start);
diff --git a/chromium/base/timer/elapsed_timer.h b/chromium/base/timer/elapsed_timer.h
index 3ea2bfadf38..1cb9f1193d8 100644
--- a/chromium/base/timer/elapsed_timer.h
+++ b/chromium/base/timer/elapsed_timer.h
@@ -15,9 +15,10 @@ namespace base {
class BASE_EXPORT ElapsedTimer {
public:
ElapsedTimer();
+ virtual ~ElapsedTimer() {}
// Returns the time elapsed since object construction.
- TimeDelta Elapsed() const;
+ virtual TimeDelta Elapsed() const;
private:
TimeTicks begin_;
diff --git a/chromium/base/timer/mock_timer.cc b/chromium/base/timer/mock_timer.cc
new file mode 100644
index 00000000000..296071e8e37
--- /dev/null
+++ b/chromium/base/timer/mock_timer.cc
@@ -0,0 +1,63 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/timer/mock_timer.h"
+
+namespace base {
+
+MockTimer::MockTimer(bool retain_user_task, bool is_repeating)
+ : Timer(retain_user_task, is_repeating),
+ is_running_(false) {
+}
+
+MockTimer::MockTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating)
+ : Timer(true, is_repeating),
+ delay_(delay),
+ is_running_(false) {
+}
+
+MockTimer::~MockTimer() {
+}
+
+bool MockTimer::IsRunning() const {
+ return is_running_;
+}
+
+base::TimeDelta MockTimer::GetCurrentDelay() const {
+ return delay_;
+}
+
+void MockTimer::Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task) {
+ delay_ = delay;
+ user_task_ = user_task;
+ Reset();
+}
+
+void MockTimer::Stop() {
+ is_running_ = false;
+ if (!retain_user_task())
+ user_task_.Reset();
+}
+
+void MockTimer::Reset() {
+ DCHECK(!user_task_.is_null());
+ is_running_ = true;
+}
+
+void MockTimer::Fire() {
+ DCHECK(is_running_);
+ base::Closure old_task = user_task_;
+ if (is_repeating())
+ Reset();
+ else
+ Stop();
+ old_task.Run();
+}
+
+} // namespace base
diff --git a/chromium/base/timer/mock_timer.h b/chromium/base/timer/mock_timer.h
new file mode 100644
index 00000000000..456cb5be90b
--- /dev/null
+++ b/chromium/base/timer/mock_timer.h
@@ -0,0 +1,41 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TIMER_MOCK_TIMER_H_
+#define BASE_TIMER_MOCK_TIMER_H_
+
+#include "base/timer/timer.h"
+
+namespace base {
+
+class BASE_EXPORT MockTimer : public Timer {
+ public:
+ MockTimer(bool retain_user_task, bool is_repeating);
+ MockTimer(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task,
+ bool is_repeating);
+ virtual ~MockTimer();
+
+ // base::Timer implementation.
+ virtual bool IsRunning() const OVERRIDE;
+ virtual base::TimeDelta GetCurrentDelay() const OVERRIDE;
+ virtual void Start(const tracked_objects::Location& posted_from,
+ base::TimeDelta delay,
+ const base::Closure& user_task) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+ virtual void Reset() OVERRIDE;
+
+ // Testing methods.
+ void Fire();
+
+ private:
+ base::Closure user_task_;
+ TimeDelta delay_;
+ bool is_running_;
+};
+
+} // namespace base
+
+#endif // !BASE_TIMER_MOCK_TIMER_H_
diff --git a/chromium/base/timer/mock_timer_unittest.cc b/chromium/base/timer/mock_timer_unittest.cc
new file mode 100644
index 00000000000..f6b6953648a
--- /dev/null
+++ b/chromium/base/timer/mock_timer_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/timer/mock_timer.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void CallMeMaybe(int *number) {
+ (*number)++;
+}
+
+TEST(MockTimerTest, FiresOnce) {
+ int calls = 0;
+ base::MockTimer timer(false, false);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ EXPECT_EQ(delay, timer.GetCurrentDelay());
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Fire();
+ EXPECT_FALSE(timer.IsRunning());
+ EXPECT_EQ(1, calls);
+}
+
+TEST(MockTimerTest, FiresRepeatedly) {
+ int calls = 0;
+ base::MockTimer timer(true, true);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ timer.Fire();
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Fire();
+ timer.Fire();
+ EXPECT_TRUE(timer.IsRunning());
+ EXPECT_EQ(3, calls);
+}
+
+TEST(MockTimerTest, Stops) {
+ int calls = 0;
+ base::MockTimer timer(true, true);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&CallMeMaybe,
+ base::Unretained(&calls)));
+ EXPECT_TRUE(timer.IsRunning());
+ timer.Stop();
+ EXPECT_FALSE(timer.IsRunning());
+}
+
+class HasWeakPtr : public base::SupportsWeakPtr<HasWeakPtr> {
+ public:
+ HasWeakPtr() {}
+ virtual ~HasWeakPtr() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HasWeakPtr);
+};
+
+void DoNothingWithWeakPtr(HasWeakPtr* has_weak_ptr) {
+}
+
+TEST(MockTimerTest, DoesNotRetainClosure) {
+ HasWeakPtr *has_weak_ptr = new HasWeakPtr();
+ base::WeakPtr<HasWeakPtr> weak_ptr(has_weak_ptr->AsWeakPtr());
+ base::MockTimer timer(false, false);
+ base::TimeDelta delay = base::TimeDelta::FromSeconds(2);
+ ASSERT_TRUE(weak_ptr.get());
+ timer.Start(FROM_HERE, delay,
+ base::Bind(&DoNothingWithWeakPtr,
+ base::Owned(has_weak_ptr)));
+ ASSERT_TRUE(weak_ptr.get());
+ timer.Fire();
+ ASSERT_FALSE(weak_ptr.get());
+}
+
+} // namespace
diff --git a/chromium/base/timer/timer.cc b/chromium/base/timer/timer.cc
index 1c4b10c6de5..1916ccc34eb 100644
--- a/chromium/base/timer/timer.cc
+++ b/chromium/base/timer/timer.cc
@@ -85,6 +85,14 @@ Timer::~Timer() {
StopAndAbandon();
}
+bool Timer::IsRunning() const {
+ return is_running_;
+}
+
+TimeDelta Timer::GetCurrentDelay() const {
+ return delay_;
+}
+
void Timer::Start(const tracked_objects::Location& posted_from,
TimeDelta delay,
const base::Closure& user_task) {
diff --git a/chromium/base/timer/timer.h b/chromium/base/timer/timer.h
index 71a7ae8888a..6d282ee8e7a 100644
--- a/chromium/base/timer/timer.h
+++ b/chromium/base/timer/timer.h
@@ -82,28 +82,24 @@ class BASE_EXPORT Timer {
virtual ~Timer();
// Returns true if the timer is running (i.e., not stopped).
- bool IsRunning() const {
- return is_running_;
- }
+ virtual bool IsRunning() const;
// Returns the current delay for this timer.
- TimeDelta GetCurrentDelay() const {
- return delay_;
- }
+ virtual TimeDelta GetCurrentDelay() const;
// Start the timer to run at the given |delay| from now. If the timer is
// already running, it will be replaced to call the given |user_task|.
- void Start(const tracked_objects::Location& posted_from,
- TimeDelta delay,
- const base::Closure& user_task);
+ virtual void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ const base::Closure& user_task);
// Call this method to stop and cancel the timer. It is a no-op if the timer
// is not running.
- void Stop();
+ virtual void Stop();
// Call this method to reset the timer delay. The user_task_ must be set. If
// the timer is not running, this will start it by posting a task.
- void Reset();
+ virtual void Reset();
const base::Closure& user_task() const { return user_task_; }
const TimeTicks& desired_run_time() const { return desired_run_time_; }
@@ -115,6 +111,9 @@ class BASE_EXPORT Timer {
TimeDelta delay,
const base::Closure& user_task);
+ bool retain_user_task() const { return retain_user_task_; }
+ bool is_repeating() const { return is_repeating_; }
+
private:
friend class BaseTimerTaskInternal;
@@ -197,10 +196,10 @@ class BaseTimerMethodPointer : public Timer {
// Start the timer to run at the given |delay| from now. If the timer is
// already running, it will be replaced to call a task formed from
// |reviewer->*method|.
- void Start(const tracked_objects::Location& posted_from,
- TimeDelta delay,
- Receiver* receiver,
- ReceiverMethod method) {
+ virtual void Start(const tracked_objects::Location& posted_from,
+ TimeDelta delay,
+ Receiver* receiver,
+ ReceiverMethod method) {
Timer::Start(posted_from, delay,
base::Bind(method, base::Unretained(receiver)));
}
diff --git a/chromium/base/timer/timer_unittest.cc b/chromium/base/timer/timer_unittest.cc
index 728e861b537..0fb2b454ad7 100644
--- a/chromium/base/timer/timer_unittest.cc
+++ b/chromium/base/timer/timer_unittest.cc
@@ -375,7 +375,7 @@ TEST(TimerTest, MessageLoopShutdown) {
OneShotTimerTester c(&did_run);
OneShotTimerTester d(&did_run);
{
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
a.Start();
b.Start();
} // MessageLoop destructs by falling out of scope.
@@ -389,7 +389,7 @@ void TimerTestCallback() {
TEST(TimerTest, NonRepeatIsRunning) {
{
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
base::Timer timer(false, false);
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
@@ -402,7 +402,7 @@ TEST(TimerTest, NonRepeatIsRunning) {
{
base::Timer timer(true, false);
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
base::Bind(&TimerTestCallback));
@@ -418,7 +418,7 @@ TEST(TimerTest, NonRepeatIsRunning) {
TEST(TimerTest, NonRepeatMessageLoopDeath) {
base::Timer timer(false, false);
{
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
EXPECT_FALSE(timer.IsRunning());
timer.Start(FROM_HERE, TimeDelta::FromDays(1),
base::Bind(&TimerTestCallback));
@@ -429,7 +429,7 @@ TEST(TimerTest, NonRepeatMessageLoopDeath) {
}
TEST(TimerTest, RetainRepeatIsRunning) {
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
base::Bind(&TimerTestCallback), true);
EXPECT_FALSE(timer.IsRunning());
@@ -442,7 +442,7 @@ TEST(TimerTest, RetainRepeatIsRunning) {
}
TEST(TimerTest, RetainNonRepeatIsRunning) {
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
base::Timer timer(FROM_HERE, TimeDelta::FromDays(1),
base::Bind(&TimerTestCallback), false);
EXPECT_FALSE(timer.IsRunning());
@@ -477,7 +477,7 @@ void SetCallbackHappened2() {
TEST(TimerTest, ContinuationStopStart) {
{
ClearAllCallbackHappened();
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
base::Timer timer(false, false);
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
base::Bind(&SetCallbackHappened1));
@@ -493,7 +493,7 @@ TEST(TimerTest, ContinuationStopStart) {
TEST(TimerTest, ContinuationReset) {
{
ClearAllCallbackHappened();
- base::MessageLoop loop(base::MessageLoop::TYPE_DEFAULT);
+ base::MessageLoop loop;
base::Timer timer(false, false);
timer.Start(FROM_HERE, TimeDelta::FromMilliseconds(10),
base::Bind(&SetCallbackHappened1));
diff --git a/chromium/base/tools_sanity_unittest.cc b/chromium/base/tools_sanity_unittest.cc
index ff789380122..7d4c96eb5f7 100644
--- a/chromium/base/tools_sanity_unittest.cc
+++ b/chromium/base/tools_sanity_unittest.cc
@@ -7,6 +7,8 @@
// errors to verify the sanity of the tools.
#include "base/atomicops.h"
+#include "base/debug/asan_invalid_access.h"
+#include "base/debug/profiler.h"
#include "base/message_loop/message_loop.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread.h"
@@ -20,29 +22,45 @@ const base::subtle::Atomic32 kMagicValue = 42;
// Helper for memory accesses that can potentially corrupt memory or cause a
// crash during a native run.
-#if defined(ADDRESS_SANITIZER)
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
#if defined(OS_IOS)
// EXPECT_DEATH is not supported on IOS.
#define HARMFUL_ACCESS(action,error_regexp) do { action; } while (0)
+#elif defined(SYZYASAN)
+// We won't get a meaningful error message because we're not running under the
+// SyzyASan logger, but we can at least make sure that the error has been
+// generated in the SyzyASan runtime.
+#define HARMFUL_ACCESS(action,unused) \
+if (debug::IsBinaryInstrumented()) { EXPECT_DEATH(action, \
+ "AsanRuntime::OnError"); }
#else
#define HARMFUL_ACCESS(action,error_regexp) EXPECT_DEATH(action,error_regexp)
-#endif // !OS_IOS
+#endif // !OS_IOS && !SYZYASAN
#else
#define HARMFUL_ACCESS(action,error_regexp) \
do { if (RunningOnValgrind()) { action; } } while (0)
#endif
-void ReadUninitializedValue(char *ptr) {
+void DoReadUninitializedValue(char *ptr) {
// Comparison with 64 is to prevent clang from optimizing away the
// jump -- valgrind only catches jumps and conditional moves, but clang uses
// the borrow flag if the condition is just `*ptr == '\0'`.
if (*ptr == 64) {
- (*ptr)++;
+ VLOG(1) << "Uninit condition is true";
} else {
- (*ptr)--;
+ VLOG(1) << "Uninit condition is false";
}
}
+void ReadUninitializedValue(char *ptr) {
+#if defined(MEMORY_SANITIZER)
+ EXPECT_DEATH(DoReadUninitializedValue(ptr),
+ "use-of-uninitialized-value");
+#else
+ DoReadUninitializedValue(ptr);
+#endif
+}
+
void ReadValueOutOfArrayBoundsLeft(char *ptr) {
char c = ptr[-2];
VLOG(1) << "Reading a byte out of bounds: " << c;
@@ -65,14 +83,15 @@ void WriteValueOutOfArrayBoundsRight(char *ptr, size_t size) {
void MakeSomeErrors(char *ptr, size_t size) {
ReadUninitializedValue(ptr);
+
HARMFUL_ACCESS(ReadValueOutOfArrayBoundsLeft(ptr),
- "heap-buffer-overflow.*2 bytes to the left");
+ "2 bytes to the left");
HARMFUL_ACCESS(ReadValueOutOfArrayBoundsRight(ptr, size),
- "heap-buffer-overflow.*1 bytes to the right");
+ "1 bytes to the right");
HARMFUL_ACCESS(WriteValueOutOfArrayBoundsLeft(ptr),
- "heap-buffer-overflow.*1 bytes to the left");
+ "1 bytes to the left");
HARMFUL_ACCESS(WriteValueOutOfArrayBoundsRight(ptr, size),
- "heap-buffer-overflow.*0 bytes to the right");
+ "0 bytes to the right");
}
} // namespace
@@ -84,10 +103,10 @@ TEST(ToolsSanityTest, MemoryLeak) {
leak[4] = 1; // Make sure the allocated memory is used.
}
-#if defined(ADDRESS_SANITIZER) && (defined(OS_IOS) || defined(OS_WIN))
+#if (defined(ADDRESS_SANITIZER) && defined(OS_IOS)) || defined(SYZYASAN)
// Because iOS doesn't support death tests, each of the following tests will
-// crash the whole program under Asan. On Windows Asan is based on SyzyAsan, the
-// error report mecanism is different than with Asan so those test will fail.
+// crash the whole program under Asan. On Windows Asan is based on SyzyAsan; the
+// error report mechanism is different than with Asan so these tests will fail.
#define MAYBE_AccessesToNewMemory DISABLED_AccessesToNewMemory
#define MAYBE_AccessesToMallocMemory DISABLED_AccessesToMallocMemory
#else
@@ -103,7 +122,7 @@ TEST(ToolsSanityTest, MemoryLeak) {
// tests should be put back under the (defined(OS_IOS) || defined(OS_WIN))
// clause above.
// See also http://crbug.com/172614.
-#if defined(ADDRESS_SANITIZER)
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
#define MAYBE_SingleElementDeletedWithBraces \
DISABLED_SingleElementDeletedWithBraces
#define MAYBE_ArrayDeletedWithoutBraces DISABLED_ArrayDeletedWithoutBraces
@@ -125,7 +144,7 @@ TEST(ToolsSanityTest, MAYBE_AccessesToMallocMemory) {
}
TEST(ToolsSanityTest, MAYBE_ArrayDeletedWithoutBraces) {
-#if !defined(ADDRESS_SANITIZER)
+#if !defined(ADDRESS_SANITIZER) && !defined(SYZYASAN)
// This test may corrupt memory if not run under Valgrind or compiled with
// AddressSanitizer.
if (!RunningOnValgrind())
@@ -151,7 +170,8 @@ TEST(ToolsSanityTest, MAYBE_SingleElementDeletedWithBraces) {
delete [] foo;
}
-#if defined(ADDRESS_SANITIZER)
+#if defined(ADDRESS_SANITIZER) || defined(SYZYASAN)
+
TEST(ToolsSanityTest, DISABLED_AddressSanitizerNullDerefCrashTest) {
// Intentionally crash to make sure AddressSanitizer is running.
// This test should not be ran on bots.
@@ -183,7 +203,31 @@ TEST(ToolsSanityTest, DISABLED_AddressSanitizerGlobalOOBCrashTest) {
*access = 43;
}
-#endif
+TEST(ToolsSanityTest, AsanHeapOverflow) {
+ HARMFUL_ACCESS(debug::AsanHeapOverflow() ,"to the right");
+}
+
+TEST(ToolsSanityTest, AsanHeapUnderflow) {
+ HARMFUL_ACCESS(debug::AsanHeapUnderflow(), "to the left");
+}
+
+TEST(ToolsSanityTest, AsanHeapUseAfterFree) {
+ HARMFUL_ACCESS(debug::AsanHeapUseAfterFree(), "heap-use-after-free");
+}
+
+#if defined(SYZYASAN)
+TEST(ToolsSanityTest, AsanCorruptHeapBlock) {
+ HARMFUL_ACCESS(debug::AsanCorruptHeapBlock(), "");
+}
+
+TEST(ToolsSanityTest, AsanCorruptHeap) {
+ // This test will kill the process by raising an exception, there's no
+ // particular string to look for in the stack trace.
+ EXPECT_DEATH(debug::AsanCorruptHeap(), "");
+}
+#endif // SYZYASAN
+
+#endif // ADDRESS_SANITIZER || SYZYASAN
namespace {
diff --git a/chromium/base/tracked_objects.cc b/chromium/base/tracked_objects.cc
index 0db7094c498..56b44c10b2c 100644
--- a/chromium/base/tracked_objects.cc
+++ b/chromium/base/tracked_objects.cc
@@ -793,11 +793,14 @@ void ThreadData::EnsureCleanupWasCalled(int major_threads_shutdown_count) {
base::AutoLock lock(*list_lock_.Pointer());
if (worker_thread_data_creation_count_ == 0)
return; // We haven't really run much, and couldn't have leaked.
+
+ // TODO(jar): until this is working on XP, don't run the real test.
+#if 0
// Verify that we've at least shutdown/cleanup the major namesd threads. The
// caller should tell us how many thread shutdowns should have taken place by
// now.
- return; // TODO(jar): until this is working on XP, don't run the real test.
CHECK_GT(cleanup_count_, major_threads_shutdown_count);
+#endif
}
// static
diff --git a/chromium/base/tracked_objects.h b/chromium/base/tracked_objects.h
index 31ce4f5fb93..511a2890661 100644
--- a/chromium/base/tracked_objects.h
+++ b/chromium/base/tracked_objects.h
@@ -362,6 +362,7 @@ class BASE_EXPORT ThreadData {
DEACTIVATED, // No longer recording profling.
PROFILING_ACTIVE, // Recording profiles (no parent-child links).
PROFILING_CHILDREN_ACTIVE, // Fully active, recording parent-child links.
+ STATUS_LAST = PROFILING_CHILDREN_ACTIVE
};
typedef std::map<Location, Births*> BirthMap;
diff --git a/chromium/base/values.cc b/chromium/base/values.cc
index 6068556bb3d..f27c09567ee 100644
--- a/chromium/base/values.cc
+++ b/chromium/base/values.cc
@@ -134,6 +134,10 @@ bool Value::GetAsString(string16* out_value) const {
return false;
}
+bool Value::GetAsString(const StringValue** out_value) const {
+ return false;
+}
+
bool Value::GetAsList(ListValue** out_value) {
return false;
}
@@ -225,13 +229,13 @@ bool FundamentalValue::GetAsDouble(double* out_value) const {
FundamentalValue* FundamentalValue::DeepCopy() const {
switch (GetType()) {
case TYPE_BOOLEAN:
- return CreateBooleanValue(boolean_value_);
+ return new FundamentalValue(boolean_value_);
case TYPE_INTEGER:
- return CreateIntegerValue(integer_value_);
+ return new FundamentalValue(integer_value_);
case TYPE_DOUBLE:
- return CreateDoubleValue(double_value_);
+ return new FundamentalValue(double_value_);
default:
NOTREACHED();
@@ -278,6 +282,14 @@ StringValue::StringValue(const string16& in_value)
StringValue::~StringValue() {
}
+std::string* StringValue::GetString() {
+ return &value_;
+}
+
+const std::string& StringValue::GetString() const {
+ return value_;
+}
+
bool StringValue::GetAsString(std::string* out_value) const {
if (out_value)
*out_value = value_;
@@ -290,8 +302,14 @@ bool StringValue::GetAsString(string16* out_value) const {
return true;
}
+bool StringValue::GetAsString(const StringValue** out_value) const {
+ if (out_value)
+ *out_value = this;
+ return true;
+}
+
StringValue* StringValue::DeepCopy() const {
- return CreateStringValue(value_);
+ return new StringValue(value_);
}
bool StringValue::Equals(const Value* other) const {
@@ -403,25 +421,25 @@ void DictionaryValue::Set(const std::string& path, Value* in_value) {
}
void DictionaryValue::SetBoolean(const std::string& path, bool in_value) {
- Set(path, CreateBooleanValue(in_value));
+ Set(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetInteger(const std::string& path, int in_value) {
- Set(path, CreateIntegerValue(in_value));
+ Set(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetDouble(const std::string& path, double in_value) {
- Set(path, CreateDoubleValue(in_value));
+ Set(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetString(const std::string& path,
const std::string& in_value) {
- Set(path, CreateStringValue(in_value));
+ Set(path, new StringValue(in_value));
}
void DictionaryValue::SetString(const std::string& path,
const string16& in_value) {
- Set(path, CreateStringValue(in_value));
+ Set(path, new StringValue(in_value));
}
void DictionaryValue::SetWithoutPathExpansion(const std::string& key,
@@ -439,27 +457,27 @@ void DictionaryValue::SetWithoutPathExpansion(const std::string& key,
void DictionaryValue::SetBooleanWithoutPathExpansion(
const std::string& path, bool in_value) {
- SetWithoutPathExpansion(path, CreateBooleanValue(in_value));
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetIntegerWithoutPathExpansion(
const std::string& path, int in_value) {
- SetWithoutPathExpansion(path, CreateIntegerValue(in_value));
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetDoubleWithoutPathExpansion(
const std::string& path, double in_value) {
- SetWithoutPathExpansion(path, CreateDoubleValue(in_value));
+ SetWithoutPathExpansion(path, new FundamentalValue(in_value));
}
void DictionaryValue::SetStringWithoutPathExpansion(
const std::string& path, const std::string& in_value) {
- SetWithoutPathExpansion(path, CreateStringValue(in_value));
+ SetWithoutPathExpansion(path, new StringValue(in_value));
}
void DictionaryValue::SetStringWithoutPathExpansion(
const std::string& path, const string16& in_value) {
- SetWithoutPathExpansion(path, CreateStringValue(in_value));
+ SetWithoutPathExpansion(path, new StringValue(in_value));
}
bool DictionaryValue::Get(const std::string& path,
@@ -1025,23 +1043,23 @@ void ListValue::Append(Value* in_value) {
}
void ListValue::AppendBoolean(bool in_value) {
- Append(CreateBooleanValue(in_value));
+ Append(new FundamentalValue(in_value));
}
void ListValue::AppendInteger(int in_value) {
- Append(CreateIntegerValue(in_value));
+ Append(new FundamentalValue(in_value));
}
void ListValue::AppendDouble(double in_value) {
- Append(CreateDoubleValue(in_value));
+ Append(new FundamentalValue(in_value));
}
void ListValue::AppendString(const std::string& in_value) {
- Append(CreateStringValue(in_value));
+ Append(new StringValue(in_value));
}
void ListValue::AppendString(const string16& in_value) {
- Append(CreateStringValue(in_value));
+ Append(new StringValue(in_value));
}
void ListValue::AppendStrings(const std::vector<std::string>& in_values) {
diff --git a/chromium/base/values.h b/chromium/base/values.h
index 65121808e64..c478e3ac79e 100644
--- a/chromium/base/values.h
+++ b/chromium/base/values.h
@@ -31,11 +31,6 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string16.h"
-// This file declares "using base::Value", etc. at the bottom, so that
-// current code can use these classes without the base namespace. In
-// new code, please always use base::Value, etc. or add your own
-// "using" declaration.
-// http://crbug.com/88666
namespace base {
class DictionaryValue;
@@ -96,6 +91,7 @@ class BASE_EXPORT Value {
virtual bool GetAsDouble(double* out_value) const;
virtual bool GetAsString(std::string* out_value) const;
virtual bool GetAsString(string16* out_value) const;
+ virtual bool GetAsString(const StringValue** out_value) const;
virtual bool GetAsList(ListValue** out_value);
virtual bool GetAsList(const ListValue** out_value) const;
virtual bool GetAsDictionary(DictionaryValue** out_value);
@@ -137,6 +133,8 @@ class BASE_EXPORT FundamentalValue : public Value {
// Overridden from Value:
virtual bool GetAsBoolean(bool* out_value) const OVERRIDE;
virtual bool GetAsInteger(int* out_value) const OVERRIDE;
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+ // doubles.
virtual bool GetAsDouble(double* out_value) const OVERRIDE;
virtual FundamentalValue* DeepCopy() const OVERRIDE;
virtual bool Equals(const Value* other) const OVERRIDE;
@@ -159,9 +157,14 @@ class BASE_EXPORT StringValue : public Value {
virtual ~StringValue();
+ // Returns |value_| as a pointer or reference.
+ std::string* GetString();
+ const std::string& GetString() const;
+
// Overridden from Value:
virtual bool GetAsString(std::string* out_value) const OVERRIDE;
virtual bool GetAsString(string16* out_value) const OVERRIDE;
+ virtual bool GetAsString(const StringValue** out_value) const OVERRIDE;
virtual StringValue* DeepCopy() const OVERRIDE;
virtual bool Equals(const Value* other) const OVERRIDE;
@@ -266,14 +269,18 @@ class BASE_EXPORT DictionaryValue : public Value {
// through the |out_value| parameter, and the function will return true.
// Otherwise, it will return false and |out_value| will be untouched.
// Note that the dictionary always owns the value that's returned.
+ // |out_value| is optional and will only be set if non-NULL.
bool Get(const std::string& path, const Value** out_value) const;
bool Get(const std::string& path, Value** out_value);
// These are convenience forms of Get(). The value will be retrieved
// and the return value will be true if the path is valid and the value at
// the end of the path can be returned in the form specified.
+ // |out_value| is optional and will only be set if non-NULL.
bool GetBoolean(const std::string& path, bool* out_value) const;
bool GetInteger(const std::string& path, int* out_value) const;
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+ // doubles.
bool GetDouble(const std::string& path, double* out_value) const;
bool GetString(const std::string& path, std::string* out_value) const;
bool GetString(const std::string& path, string16* out_value) const;
@@ -399,14 +406,18 @@ class BASE_EXPORT ListValue : public Value {
// Gets the Value at the given index. Modifies |out_value| (and returns true)
// only if the index falls within the current list range.
// Note that the list always owns the Value passed out via |out_value|.
+ // |out_value| is optional and will only be set if non-NULL.
bool Get(size_t index, const Value** out_value) const;
bool Get(size_t index, Value** out_value);
// Convenience forms of Get(). Modifies |out_value| (and returns true)
// only if the index is valid and the Value at that index can be returned
// in the specified form.
+ // |out_value| is optional and will only be set if non-NULL.
bool GetBoolean(size_t index, bool* out_value) const;
bool GetInteger(size_t index, int* out_value) const;
+ // Values of both type TYPE_INTEGER and TYPE_DOUBLE can be obtained as
+ // doubles.
bool GetDouble(size_t index, double* out_value) const;
bool GetString(size_t index, std::string* out_value) const;
bool GetString(size_t index, string16* out_value) const;
@@ -528,10 +539,4 @@ BASE_EXPORT inline std::ostream& operator<<(std::ostream& out,
} // namespace base
-// http://crbug.com/88666
-using base::DictionaryValue;
-using base::ListValue;
-using base::StringValue;
-using base::Value;
-
#endif // BASE_VALUES_H_
diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc
index 70acdfd6966..c8a6cbf9a77 100644
--- a/chromium/base/values_unittest.cc
+++ b/chromium/base/values_unittest.cc
@@ -130,7 +130,7 @@ TEST(ValuesTest, BinaryValue) {
}
TEST(ValuesTest, StringValue) {
- // Test overloaded CreateStringValue.
+ // Test overloaded StringValue constructor.
scoped_ptr<Value> narrow_value(new StringValue("narrow"));
ASSERT_TRUE(narrow_value.get());
ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
@@ -138,18 +138,29 @@ TEST(ValuesTest, StringValue) {
ASSERT_TRUE(utf16_value.get());
ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
- // Test overloaded GetString.
+ // Test overloaded GetAsString.
std::string narrow = "http://google.com";
string16 utf16 = ASCIIToUTF16("http://google.com");
+ const StringValue* string_value = NULL;
ASSERT_TRUE(narrow_value->GetAsString(&narrow));
ASSERT_TRUE(narrow_value->GetAsString(&utf16));
+ ASSERT_TRUE(narrow_value->GetAsString(&string_value));
ASSERT_EQ(std::string("narrow"), narrow);
ASSERT_EQ(ASCIIToUTF16("narrow"), utf16);
+ ASSERT_EQ(string_value->GetString(), narrow);
ASSERT_TRUE(utf16_value->GetAsString(&narrow));
ASSERT_TRUE(utf16_value->GetAsString(&utf16));
+ ASSERT_TRUE(utf16_value->GetAsString(&string_value));
ASSERT_EQ(std::string("utf16"), narrow);
ASSERT_EQ(ASCIIToUTF16("utf16"), utf16);
+ ASSERT_EQ(string_value->GetString(), narrow);
+
+ // Don't choke on NULL values.
+ ASSERT_TRUE(narrow_value->GetAsString(static_cast<string16*>(NULL)));
+ ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(NULL)));
+ ASSERT_TRUE(narrow_value->GetAsString(
+ static_cast<const StringValue**>(NULL)));
}
// This is a Value object that allows us to tell if it's been
@@ -325,8 +336,8 @@ TEST(ValuesTest, DictionaryWithoutPathExpansion) {
TEST(ValuesTest, DictionaryRemovePath) {
DictionaryValue dict;
- dict.Set("a.long.way.down", Value::CreateIntegerValue(1));
- dict.Set("a.long.key.path", Value::CreateBooleanValue(true));
+ dict.Set("a.long.way.down", new FundamentalValue(1));
+ dict.Set("a.long.key.path", new FundamentalValue(true));
scoped_ptr<Value> removed_item;
EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item));
@@ -798,4 +809,289 @@ TEST(ValuesTest, DictionaryIterator) {
EXPECT_TRUE(seen2);
}
+// DictionaryValue/ListValue's Get*() methods should accept NULL as an out-value
+// and still return true/false based on success.
+TEST(ValuesTest, GetWithNullOutValue) {
+ DictionaryValue main_dict;
+ ListValue main_list;
+
+ FundamentalValue bool_value(false);
+ FundamentalValue int_value(1234);
+ FundamentalValue double_value(12.34567);
+ StringValue string_value("foo");
+ BinaryValue binary_value;
+ DictionaryValue dict_value;
+ ListValue list_value;
+
+ main_dict.Set("bool", bool_value.DeepCopy());
+ main_dict.Set("int", int_value.DeepCopy());
+ main_dict.Set("double", double_value.DeepCopy());
+ main_dict.Set("string", string_value.DeepCopy());
+ main_dict.Set("binary", binary_value.DeepCopy());
+ main_dict.Set("dict", dict_value.DeepCopy());
+ main_dict.Set("list", list_value.DeepCopy());
+
+ main_list.Append(bool_value.DeepCopy());
+ main_list.Append(int_value.DeepCopy());
+ main_list.Append(double_value.DeepCopy());
+ main_list.Append(string_value.DeepCopy());
+ main_list.Append(binary_value.DeepCopy());
+ main_list.Append(dict_value.DeepCopy());
+ main_list.Append(list_value.DeepCopy());
+
+ EXPECT_TRUE(main_dict.Get("bool", NULL));
+ EXPECT_TRUE(main_dict.Get("int", NULL));
+ EXPECT_TRUE(main_dict.Get("double", NULL));
+ EXPECT_TRUE(main_dict.Get("string", NULL));
+ EXPECT_TRUE(main_dict.Get("binary", NULL));
+ EXPECT_TRUE(main_dict.Get("dict", NULL));
+ EXPECT_TRUE(main_dict.Get("list", NULL));
+ EXPECT_FALSE(main_dict.Get("DNE", NULL));
+
+ EXPECT_TRUE(main_dict.GetBoolean("bool", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("int", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("double", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("string", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("binary", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("dict", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("list", NULL));
+ EXPECT_FALSE(main_dict.GetBoolean("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetInteger("bool", NULL));
+ EXPECT_TRUE(main_dict.GetInteger("int", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("double", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("string", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("binary", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("dict", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("list", NULL));
+ EXPECT_FALSE(main_dict.GetInteger("DNE", NULL));
+
+ // Both int and double values can be obtained from GetDouble.
+ EXPECT_FALSE(main_dict.GetDouble("bool", NULL));
+ EXPECT_TRUE(main_dict.GetDouble("int", NULL));
+ EXPECT_TRUE(main_dict.GetDouble("double", NULL));
+ EXPECT_FALSE(main_dict.GetDouble("string", NULL));
+ EXPECT_FALSE(main_dict.GetDouble("binary", NULL));
+ EXPECT_FALSE(main_dict.GetDouble("dict", NULL));
+ EXPECT_FALSE(main_dict.GetDouble("list", NULL));
+ EXPECT_FALSE(main_dict.GetDouble("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetString("bool", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("int", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("double", static_cast<std::string*>(NULL)));
+ EXPECT_TRUE(main_dict.GetString("string", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("binary", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("dict", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("list", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("DNE", static_cast<std::string*>(NULL)));
+
+ EXPECT_FALSE(main_dict.GetString("bool", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("int", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("double", static_cast<string16*>(NULL)));
+ EXPECT_TRUE(main_dict.GetString("string", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("binary", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("dict", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("list", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetString("DNE", static_cast<string16*>(NULL)));
+
+ EXPECT_FALSE(main_dict.GetBinary("bool", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("int", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("double", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("string", NULL));
+ EXPECT_TRUE(main_dict.GetBinary("binary", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("dict", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("list", NULL));
+ EXPECT_FALSE(main_dict.GetBinary("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetDictionary("bool", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("int", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("double", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("string", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("binary", NULL));
+ EXPECT_TRUE(main_dict.GetDictionary("dict", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("list", NULL));
+ EXPECT_FALSE(main_dict.GetDictionary("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetList("bool", NULL));
+ EXPECT_FALSE(main_dict.GetList("int", NULL));
+ EXPECT_FALSE(main_dict.GetList("double", NULL));
+ EXPECT_FALSE(main_dict.GetList("string", NULL));
+ EXPECT_FALSE(main_dict.GetList("binary", NULL));
+ EXPECT_FALSE(main_dict.GetList("dict", NULL));
+ EXPECT_TRUE(main_dict.GetList("list", NULL));
+ EXPECT_FALSE(main_dict.GetList("DNE", NULL));
+
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("bool", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("int", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("double", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("string", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("binary", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("dict", NULL));
+ EXPECT_TRUE(main_dict.GetWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_TRUE(main_dict.GetBooleanWithoutPathExpansion("bool", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("int", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("double", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("string", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("binary", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("dict", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("bool", NULL));
+ EXPECT_TRUE(main_dict.GetIntegerWithoutPathExpansion("int", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("double", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("string", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("binary", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("dict", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("bool", NULL));
+ EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("int", NULL));
+ EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("double", NULL));
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("string", NULL));
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("binary", NULL));
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("dict", NULL));
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "bool", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "int", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "double", static_cast<std::string*>(NULL)));
+ EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion(
+ "string", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "binary", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "dict", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "list", static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "DNE", static_cast<std::string*>(NULL)));
+
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "bool", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "int", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "double", static_cast<string16*>(NULL)));
+ EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion(
+ "string", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "binary", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "dict", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "list", static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+ "DNE", static_cast<string16*>(NULL)));
+
+ // There is no GetBinaryWithoutPathExpansion for some reason, but if there
+ // were it should be tested here...
+
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("bool", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("int", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("double", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("string", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("binary", NULL));
+ EXPECT_TRUE(main_dict.GetDictionaryWithoutPathExpansion("dict", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("bool", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("int", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("double", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("string", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("binary", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("dict", NULL));
+ EXPECT_TRUE(main_dict.GetListWithoutPathExpansion("list", NULL));
+ EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("DNE", NULL));
+
+ EXPECT_TRUE(main_list.Get(0, NULL));
+ EXPECT_TRUE(main_list.Get(1, NULL));
+ EXPECT_TRUE(main_list.Get(2, NULL));
+ EXPECT_TRUE(main_list.Get(3, NULL));
+ EXPECT_TRUE(main_list.Get(4, NULL));
+ EXPECT_TRUE(main_list.Get(5, NULL));
+ EXPECT_TRUE(main_list.Get(6, NULL));
+ EXPECT_FALSE(main_list.Get(7, NULL));
+
+ EXPECT_TRUE(main_list.GetBoolean(0, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(1, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(2, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(3, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(4, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(5, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(6, NULL));
+ EXPECT_FALSE(main_list.GetBoolean(7, NULL));
+
+ EXPECT_FALSE(main_list.GetInteger(0, NULL));
+ EXPECT_TRUE(main_list.GetInteger(1, NULL));
+ EXPECT_FALSE(main_list.GetInteger(2, NULL));
+ EXPECT_FALSE(main_list.GetInteger(3, NULL));
+ EXPECT_FALSE(main_list.GetInteger(4, NULL));
+ EXPECT_FALSE(main_list.GetInteger(5, NULL));
+ EXPECT_FALSE(main_list.GetInteger(6, NULL));
+ EXPECT_FALSE(main_list.GetInteger(7, NULL));
+
+ EXPECT_FALSE(main_list.GetDouble(0, NULL));
+ EXPECT_TRUE(main_list.GetDouble(1, NULL));
+ EXPECT_TRUE(main_list.GetDouble(2, NULL));
+ EXPECT_FALSE(main_list.GetDouble(3, NULL));
+ EXPECT_FALSE(main_list.GetDouble(4, NULL));
+ EXPECT_FALSE(main_list.GetDouble(5, NULL));
+ EXPECT_FALSE(main_list.GetDouble(6, NULL));
+ EXPECT_FALSE(main_list.GetDouble(7, NULL));
+
+ EXPECT_FALSE(main_list.GetString(0, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(1, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(2, static_cast<std::string*>(NULL)));
+ EXPECT_TRUE(main_list.GetString(3, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(4, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(5, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(6, static_cast<std::string*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(7, static_cast<std::string*>(NULL)));
+
+ EXPECT_FALSE(main_list.GetString(0, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(1, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(2, static_cast<string16*>(NULL)));
+ EXPECT_TRUE(main_list.GetString(3, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(4, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(5, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(6, static_cast<string16*>(NULL)));
+ EXPECT_FALSE(main_list.GetString(7, static_cast<string16*>(NULL)));
+
+ EXPECT_FALSE(main_list.GetBinary(0, NULL));
+ EXPECT_FALSE(main_list.GetBinary(1, NULL));
+ EXPECT_FALSE(main_list.GetBinary(2, NULL));
+ EXPECT_FALSE(main_list.GetBinary(3, NULL));
+ EXPECT_TRUE(main_list.GetBinary(4, NULL));
+ EXPECT_FALSE(main_list.GetBinary(5, NULL));
+ EXPECT_FALSE(main_list.GetBinary(6, NULL));
+ EXPECT_FALSE(main_list.GetBinary(7, NULL));
+
+ EXPECT_FALSE(main_list.GetDictionary(0, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(1, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(2, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(3, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(4, NULL));
+ EXPECT_TRUE(main_list.GetDictionary(5, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(6, NULL));
+ EXPECT_FALSE(main_list.GetDictionary(7, NULL));
+
+ EXPECT_FALSE(main_list.GetList(0, NULL));
+ EXPECT_FALSE(main_list.GetList(1, NULL));
+ EXPECT_FALSE(main_list.GetList(2, NULL));
+ EXPECT_FALSE(main_list.GetList(3, NULL));
+ EXPECT_FALSE(main_list.GetList(4, NULL));
+ EXPECT_FALSE(main_list.GetList(5, NULL));
+ EXPECT_TRUE(main_list.GetList(6, NULL));
+ EXPECT_FALSE(main_list.GetList(7, NULL));
+}
+
} // namespace base
diff --git a/chromium/base/win/OWNERS b/chromium/base/win/OWNERS
index 3aae3d6bec8..65ed72168fd 100644
--- a/chromium/base/win/OWNERS
+++ b/chromium/base/win/OWNERS
@@ -1 +1,2 @@
cpu@chromium.org
+rvargas@chromium.org
diff --git a/chromium/base/win/dllmain.cc b/chromium/base/win/dllmain.cc
index 9d2a6dc76ce..907c7f4034e 100644
--- a/chromium/base/win/dllmain.cc
+++ b/chromium/base/win/dllmain.cc
@@ -54,9 +54,9 @@ static void NTAPI on_callback(PVOID h, DWORD reason, PVOID reserved);
#endif // _WIN64
-// Explicitly depend on tlssup.cc variable to bracket the list of TLS callbacks.
-extern "C" PIMAGE_TLS_CALLBACK __xl_a;
-extern "C" PIMAGE_TLS_CALLBACK __xl_z;
+// Explicitly depend on VC\crt\src\tlssup.c variables
+// to bracket the list of TLS callbacks.
+extern "C" PIMAGE_TLS_CALLBACK __xl_a, __xl_z;
// extern "C" suppresses C++ name mangling so we know the symbol names for the
// linker /INCLUDE:symbol pragmas above.
diff --git a/chromium/base/win/event_trace_consumer.h b/chromium/base/win/event_trace_consumer.h
index c1b42b4fd93..9322e1e9b4f 100644
--- a/chromium/base/win/event_trace_consumer.h
+++ b/chromium/base/win/event_trace_consumer.h
@@ -16,7 +16,7 @@ namespace base {
namespace win {
// This class is a base class that makes it easier to consume events
-// from realtime or file sessions. Concrete consumers need to sublass
+// from realtime or file sessions. Concrete consumers need to subclass
// a specialization of this class and override the ProcessEvent and/or
// the ProcessBuffer methods to implement the event consumption logic.
// Usage might look like:
diff --git a/chromium/base/win/event_trace_consumer_unittest.cc b/chromium/base/win/event_trace_consumer_unittest.cc
index d238192c4f8..9066a7c8ff0 100644
--- a/chromium/base/win/event_trace_consumer_unittest.cc
+++ b/chromium/base/win/event_trace_consumer_unittest.cc
@@ -43,10 +43,9 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
}
void ClearQueue() {
- EventQueue::const_iterator it(events_.begin()), end(events_.end());
-
- for (; it != end; ++it) {
- delete [] it->MofData;
+ for (EventQueue::const_iterator it(events_.begin()), end(events_.end());
+ it != end; ++it) {
+ delete[] it->MofData;
}
events_.clear();
@@ -56,7 +55,7 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> {
events_.push_back(*event);
EVENT_TRACE& back = events_.back();
- if (NULL != event->MofData && 0 != event->MofLength) {
+ if (event->MofData != NULL && event->MofLength != 0) {
back.MofData = new char[event->MofLength];
memcpy(back.MofData, event->MofData, event->MofLength);
}
@@ -94,7 +93,7 @@ class EtwTraceConsumerBaseTest: public testing::Test {
}
virtual void TearDown() {
- // Cleanup any potentially danging sessions.
+ // Cleanup any potentially dangling sessions.
EtwTraceProperties ignore;
EtwTraceController::Stop(session_name_.c_str(), &ignore);
}
@@ -112,14 +111,12 @@ TEST_F(EtwTraceConsumerBaseTest, Initialize) {
TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) {
TestConsumer consumer_;
-
ASSERT_HRESULT_SUCCEEDED(
consumer_.OpenRealtimeSession(session_name_.c_str()));
}
TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) {
TestConsumer consumer_;
-
ASSERT_HRESULT_SUCCEEDED(
consumer_.OpenRealtimeSession(session_name_.c_str()));
ASSERT_HRESULT_FAILED(consumer_.Consume());
@@ -131,22 +128,18 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
public:
virtual void SetUp() {
EtwTraceConsumerBaseTest::SetUp();
-
ASSERT_HRESULT_SUCCEEDED(
consumer_.OpenRealtimeSession(session_name_.c_str()));
}
virtual void TearDown() {
consumer_.Close();
-
EtwTraceConsumerBaseTest::TearDown();
}
DWORD ConsumerThread() {
::SetEvent(consumer_ready_.Get());
-
- HRESULT hr = consumer_.Consume();
- return hr;
+ return consumer_.Consume();
}
static DWORD WINAPI ConsumerThreadMainProc(void* arg) {
@@ -157,12 +150,11 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
HRESULT StartConsumerThread() {
consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL));
EXPECT_TRUE(consumer_ready_ != NULL);
- consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc,
- this, 0, NULL));
- if (NULL == consumer_thread_.Get())
+ consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, this,
+ 0, NULL));
+ if (consumer_thread_.Get() == NULL)
return HRESULT_FROM_WIN32(::GetLastError());
- HRESULT hr = S_OK;
HANDLE events[] = { consumer_ready_, consumer_thread_ };
DWORD result = ::WaitForMultipleObjects(arraysize(events), events,
FALSE, INFINITE);
@@ -173,26 +165,21 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
case WAIT_OBJECT_0 + 1: {
// The thread finished. This may race with the event, so check
// explicitly for the event here, before concluding there's trouble.
- if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0))
+ if (::WaitForSingleObject(consumer_ready_, 0) == WAIT_OBJECT_0)
return S_OK;
DWORD exit_code = 0;
if (::GetExitCodeThread(consumer_thread_, &exit_code))
return exit_code;
- else
- return HRESULT_FROM_WIN32(::GetLastError());
- break;
+ return HRESULT_FROM_WIN32(::GetLastError());
}
default:
return E_UNEXPECTED;
- break;
}
-
- return hr;
}
// Waits for consumer_ thread to exit, and returns its exit code.
HRESULT JoinConsumerThread() {
- if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE))
+ if (::WaitForSingleObject(consumer_thread_, INFINITE) != WAIT_OBJECT_0)
return HRESULT_FROM_WIN32(::GetLastError());
DWORD exit_code = 0;
@@ -211,10 +198,8 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest {
TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
EtwTraceController controller;
-
- HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
- 100 * 1024);
- if (hr == E_ACCESSDENIED) {
+ if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) ==
+ E_ACCESSDENIED) {
VLOG(1) << "You must be an administrator to run this test on Vista";
return;
}
@@ -224,7 +209,6 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
// Wait around for the consumer_ thread a bit.
ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50));
-
ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
// The consumer_ returns success on session stop.
@@ -234,34 +218,32 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) {
namespace {
// {57E47923-A549-476f-86CA-503D57F59E62}
-DEFINE_GUID(kTestEventType,
- 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
+DEFINE_GUID(
+ kTestEventType,
+ 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62);
} // namespace
TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) {
EtwTraceController controller;
- HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(),
- 100 * 1024);
- if (hr == E_ACCESSDENIED) {
+ if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) ==
+ E_ACCESSDENIED) {
VLOG(1) << "You must be an administrator to run this test on Vista";
return;
}
- ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
- TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+ ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(
+ test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
EtwTraceProvider provider(test_provider_);
ASSERT_EQ(ERROR_SUCCESS, provider.Register());
// Start the consumer_.
ASSERT_HRESULT_SUCCEEDED(StartConsumerThread());
-
ASSERT_EQ(0, TestConsumer::events_.size());
EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR);
EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header));
-
EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_,
INFINITE));
ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL));
@@ -306,8 +288,8 @@ class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest {
return hr;
// Enable our provider.
- EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_,
- TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
+ EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(
+ test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF));
EtwTraceProvider provider(test_provider_);
// Then register our provider, means we get a session handle immediately.
@@ -374,7 +356,7 @@ TEST_F(EtwTraceConsumerDataTest, RoundTrip) {
return;
}
ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed";
- ASSERT_TRUE(NULL != trace);
+ ASSERT_TRUE(trace != NULL);
ASSERT_EQ(sizeof(kData), trace->MofLength);
ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData));
}
diff --git a/chromium/base/win/event_trace_controller.cc b/chromium/base/win/event_trace_controller.cc
index 0391fbc3016..9a35a6bd57a 100644
--- a/chromium/base/win/event_trace_controller.cc
+++ b/chromium/base/win/event_trace_controller.cc
@@ -64,7 +64,7 @@ HRESULT EtwTraceController::Start(const wchar_t* session_name,
}
HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name,
- const wchar_t* logfile_path, bool realtime) {
+ const wchar_t* logfile_path, bool realtime) {
DCHECK(NULL == session_ && session_name_.empty());
EtwTraceProperties prop;
diff --git a/chromium/base/win/iat_patch_function.cc b/chromium/base/win/iat_patch_function.cc
index a4a89028b87..21c39950cc3 100644
--- a/chromium/base/win/iat_patch_function.cc
+++ b/chromium/base/win/iat_patch_function.cc
@@ -56,11 +56,23 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) {
}
// Change the page protection so that we can write.
+ MEMORY_BASIC_INFORMATION memory_info;
DWORD error = NO_ERROR;
DWORD old_page_protection = 0;
+
+ if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) {
+ error = GetLastError();
+ return error;
+ }
+
+ DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ |
+ PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) &
+ memory_info.Protect;
+
if (VirtualProtect(old_code,
length,
- PAGE_READWRITE,
+ is_executable ? PAGE_EXECUTE_READWRITE :
+ PAGE_READWRITE,
&old_page_protection)) {
// Write the data.
@@ -74,7 +86,6 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) {
&old_page_protection);
} else {
error = GetLastError();
- NOTREACHED();
}
return error;
@@ -274,5 +285,10 @@ DWORD IATPatchFunction::Unpatch() {
return error;
}
+void* IATPatchFunction::original_function() const {
+ DCHECK(is_patched());
+ return original_function_;
+}
+
} // namespace win
} // namespace base
diff --git a/chromium/base/win/iat_patch_function.h b/chromium/base/win/iat_patch_function.h
index 3ae1f3c460a..5026e0eb954 100644
--- a/chromium/base/win/iat_patch_function.h
+++ b/chromium/base/win/iat_patch_function.h
@@ -57,6 +57,8 @@ class BASE_EXPORT IATPatchFunction {
return (NULL != intercept_function_);
}
+ void* original_function() const;
+
private:
HMODULE module_handle_;
void* intercept_function_;
diff --git a/chromium/base/win/message_window.cc b/chromium/base/win/message_window.cc
index 56660740fc1..57fe64c7981 100644
--- a/chromium/base/win/message_window.cc
+++ b/chromium/base/win/message_window.cc
@@ -52,7 +52,7 @@ MessageWindow::WindowClass::WindowClass()
window_class.hIconSm = NULL;
atom_ = RegisterClassEx(&window_class);
if (atom_ == 0) {
- LOG_GETLASTERROR(ERROR)
+ PLOG(ERROR)
<< "Failed to register the window class for a message-only window";
}
}
@@ -108,7 +108,7 @@ bool MessageWindow::DoCreate(const MessageCallback& message_callback,
window_ = CreateWindow(MAKEINTATOM(window_class.atom()), window_name, 0, 0, 0,
0, 0, HWND_MESSAGE, 0, window_class.instance(), this);
if (!window_) {
- LOG_GETLASTERROR(ERROR) << "Failed to create a message-only window";
+ PLOG(ERROR) << "Failed to create a message-only window";
return false;
}
diff --git a/chromium/base/win/metro.cc b/chromium/base/win/metro.cc
index c78cc0946ee..62743c799b5 100644
--- a/chromium/base/win/metro.cc
+++ b/chromium/base/win/metro.cc
@@ -12,10 +12,6 @@
namespace base {
namespace win {
-namespace {
-bool g_should_tsf_aware_required = false;
-}
-
HMODULE GetMetroModule() {
const HMODULE kUninitialized = reinterpret_cast<HMODULE>(1);
static HMODULE metro_module = kUninitialized;
@@ -70,40 +66,6 @@ bool IsProcessImmersive(HANDLE process) {
return false;
}
-bool IsTSFAwareRequired() {
- // Although this function is equal to IsMetroProcess at this moment,
- // Chrome for Win7 and Vista may support TSF in the future.
- return g_should_tsf_aware_required || IsMetroProcess();
-}
-
-void SetForceToUseTSF() {
- g_should_tsf_aware_required = true;
-
- // Since Windows 8 Metro mode disables CUAS (Cicero Unaware Application
- // Support) via ImmDisableLegacyIME API, Chrome must be fully TSF-aware on
- // Metro mode. For debugging purposes, explicitly call ImmDisableLegacyIME so
- // that one can test TSF functionality even on Windows 8 desktop mode. Note
- // that CUAS cannot be disabled on Windows Vista/7 where ImmDisableLegacyIME
- // is not available.
- typedef BOOL (* ImmDisableLegacyIMEFunc)();
- HMODULE imm32 = ::GetModuleHandleA("imm32.dll");
- if (imm32 == NULL)
- return;
-
- ImmDisableLegacyIMEFunc imm_disable_legacy_ime =
- reinterpret_cast<ImmDisableLegacyIMEFunc>(
- ::GetProcAddress(imm32, "ImmDisableLegacyIME"));
-
- if (imm_disable_legacy_ime == NULL) {
- // Unsupported API, just do nothing.
- return;
- }
-
- if (!imm_disable_legacy_ime()) {
- DVLOG(1) << "Failed to disable legacy IME.";
- }
-}
-
wchar_t* LocalAllocAndCopyString(const string16& src) {
size_t dest_size = (src.length() + 1) * sizeof(wchar_t);
wchar_t* dest = reinterpret_cast<wchar_t*>(LocalAlloc(LPTR, dest_size));
diff --git a/chromium/base/win/metro.h b/chromium/base/win/metro.h
index b2208fcb49c..5894ef06dae 100644
--- a/chromium/base/win/metro.h
+++ b/chromium/base/win/metro.h
@@ -76,17 +76,6 @@ BASE_EXPORT bool IsMetroProcess();
// immersive (Metro) process.
BASE_EXPORT bool IsProcessImmersive(HANDLE process);
-// Returns true if this process is running under Text Services Framework (TSF)
-// and browser must be TSF-aware.
-BASE_EXPORT bool IsTSFAwareRequired();
-
-// Sets browser to use Text Services Framework (TSF) regardless of process
-// status. On Windows 8, this function also disables CUAS (Cicero Unaware
-// Application Support) to emulate Windows Metro mode in terms of IME
-// functionality. This should be beneficial in QA process because on can test
-// IME functionality in Windows 8 desktop mode.
-BASE_EXPORT void SetForceToUseTSF();
-
// Allocates and returns the destination string via the LocalAlloc API after
// copying the src to it.
BASE_EXPORT wchar_t* LocalAllocAndCopyString(const string16& src);
diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc
index 078f5b9fa1c..3bb1cd32c52 100644
--- a/chromium/base/win/object_watcher.cc
+++ b/chromium/base/win/object_watcher.cc
@@ -43,7 +43,7 @@ bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
if (!RegisterWaitForSingleObject(&wait_object_, object, DoneWaiting,
this, INFINITE, wait_flags)) {
- DLOG_GETLASTERROR(FATAL) << "RegisterWaitForSingleObject failed";
+ DPLOG(FATAL) << "RegisterWaitForSingleObject failed";
object_ = NULL;
wait_object_ = NULL;
return false;
@@ -65,7 +65,7 @@ bool ObjectWatcher::StopWatching() {
// Blocking call to cancel the wait. Any callbacks already in progress will
// finish before we return from this call.
if (!UnregisterWaitEx(wait_object_, INVALID_HANDLE_VALUE)) {
- DLOG_GETLASTERROR(FATAL) << "UnregisterWaitEx failed";
+ DPLOG(FATAL) << "UnregisterWaitEx failed";
return false;
}
diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc
index 8bfe4329711..a6cb9ae89f7 100644
--- a/chromium/base/win/registry.cc
+++ b/chromium/base/win/registry.cc
@@ -10,8 +10,7 @@
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
-
-#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey
+#include "base/win/windows_version.h"
namespace base {
namespace win {
@@ -30,23 +29,29 @@ inline DWORD to_wchar_size(DWORD byte_size) {
return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
}
+// Mask to pull WOW64 access flags out of REGSAM access.
+const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
+
} // namespace
// RegKey ----------------------------------------------------------------------
RegKey::RegKey()
: key_(NULL),
- watch_event_(0) {
+ watch_event_(0),
+ wow64access_(0) {
}
RegKey::RegKey(HKEY key)
: key_(key),
- watch_event_(0) {
+ watch_event_(0),
+ wow64access_(0) {
}
RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
: key_(NULL),
- watch_event_(0) {
+ watch_event_(0),
+ wow64access_(0) {
if (rootkey) {
if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
Create(rootkey, subkey, access);
@@ -54,6 +59,7 @@ RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
Open(rootkey, subkey, access);
} else {
DCHECK(!subkey);
+ wow64access_ = access & kWow64AccessMask;
}
}
@@ -69,43 +75,77 @@ LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
DWORD* disposition, REGSAM access) {
DCHECK(rootkey && subkey && access && disposition);
- Close();
-
+ HKEY subhkey = NULL;
LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
- REG_OPTION_NON_VOLATILE, access, NULL, &key_,
+ REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
disposition);
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subhkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
+
return result;
}
LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
DCHECK(name && access);
+ // After the application has accessed an alternate registry view using one of
+ // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
+ // (create, delete, or open) on child registry keys must explicitly use the
+ // same flag. Otherwise, there can be unexpected behavior.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ if ((access & kWow64AccessMask) != wow64access_) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
HKEY subkey = NULL;
LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
access, NULL, &subkey, NULL);
- Close();
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
- key_ = subkey;
return result;
}
LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
DCHECK(rootkey && subkey && access);
- Close();
+ HKEY subhkey = NULL;
+
+ LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subhkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
- LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
return result;
}
LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
DCHECK(relative_key_name && access);
+ // After the application has accessed an alternate registry view using one of
+ // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
+ // (create, delete, or open) on child registry keys must explicitly use the
+ // same flag. Otherwise, there can be unexpected behavior.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
+ if ((access & kWow64AccessMask) != wow64access_) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
HKEY subkey = NULL;
LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
// We have to close the current opened key before replacing it with the new
// one.
- Close();
-
- key_ = subkey;
+ if (result == ERROR_SUCCESS) {
+ Close();
+ key_ = subkey;
+ wow64access_ = access & kWow64AccessMask;
+ }
return result;
}
@@ -114,9 +154,11 @@ void RegKey::Close() {
if (key_) {
::RegCloseKey(key_);
key_ = NULL;
+ wow64access_ = 0;
}
}
+// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
void RegKey::Set(HKEY key) {
if (key_ != key) {
Close();
@@ -125,6 +167,7 @@ void RegKey::Set(HKEY key) {
}
HKEY RegKey::Take() {
+ DCHECK(wow64access_ == 0);
StopWatching();
HKEY key = key_;
key_ = NULL;
@@ -155,8 +198,43 @@ LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
LONG RegKey::DeleteKey(const wchar_t* name) {
DCHECK(key_);
DCHECK(name);
- LONG result = SHDeleteKey(key_, name);
- return result;
+ HKEY subkey = NULL;
+
+ // Verify the key exists before attempting delete to replicate previous
+ // behavior.
+ LONG result =
+ RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
+ if (result != ERROR_SUCCESS)
+ return result;
+ RegCloseKey(subkey);
+
+ return RegDelRecurse(key_, std::wstring(name), wow64access_);
+}
+
+LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
+ DCHECK(key_);
+ DCHECK(name);
+
+ HKEY target_key = NULL;
+ LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
+ &target_key);
+
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ DWORD count = 0;
+ result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+
+ RegCloseKey(target_key);
+
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ if (count == 0)
+ return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
+
+ return ERROR_DIR_NOT_EMPTY;
}
LONG RegKey::DeleteValue(const wchar_t* value_name) {
@@ -329,6 +407,83 @@ LONG RegKey::StopWatching() {
return result;
}
+// static
+LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
+ const wchar_t* lpSubKey,
+ REGSAM samDesired,
+ DWORD Reserved) {
+ typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
+
+ RegDeleteKeyExPtr reg_delete_key_ex_func =
+ reinterpret_cast<RegDeleteKeyExPtr>(
+ GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
+
+ if (reg_delete_key_ex_func)
+ return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
+
+ // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
+ return RegDeleteKey(hKey, lpSubKey);
+}
+
+// static
+LONG RegKey::RegDelRecurse(HKEY root_key,
+ const std::wstring& name,
+ REGSAM access) {
+ // First, see if the key can be deleted without having to recurse.
+ LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
+ if (result == ERROR_SUCCESS)
+ return result;
+
+ HKEY target_key = NULL;
+ result = RegOpenKeyEx(
+ root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
+
+ if (result == ERROR_FILE_NOT_FOUND)
+ return ERROR_SUCCESS;
+ if (result != ERROR_SUCCESS)
+ return result;
+
+ std::wstring subkey_name(name);
+
+ // Check for an ending slash and add one if it is missing.
+ if (!name.empty() && subkey_name[name.length() - 1] != L'\\')
+ subkey_name += L"\\";
+
+ // Enumerate the keys
+ result = ERROR_SUCCESS;
+ const DWORD kMaxKeyNameLength = MAX_PATH;
+ const size_t base_key_length = subkey_name.length();
+ std::wstring key_name;
+ while (result == ERROR_SUCCESS) {
+ DWORD key_size = kMaxKeyNameLength;
+ result = RegEnumKeyEx(target_key,
+ 0,
+ WriteInto(&key_name, kMaxKeyNameLength),
+ &key_size,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if (result != ERROR_SUCCESS)
+ break;
+
+ key_name.resize(key_size);
+ subkey_name.resize(base_key_length);
+ subkey_name += key_name;
+
+ if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
+ break;
+ }
+
+ RegCloseKey(target_key);
+
+ // Try again to delete the key.
+ result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
+
+ return result;
+}
+
// RegistryValueIterator ------------------------------------------------------
RegistryValueIterator::RegistryValueIterator(HKEY root_key,
diff --git a/chromium/base/win/registry.h b/chromium/base/win/registry.h
index f97f4f5a379..af1aee7dce9 100644
--- a/chromium/base/win/registry.h
+++ b/chromium/base/win/registry.h
@@ -53,11 +53,11 @@ class BASE_EXPORT RegKey {
// Transfers ownership away from this object.
HKEY Take();
- // Returns false if this key does not have the specified value, of if an error
+ // Returns false if this key does not have the specified value, or if an error
// occurrs while attempting to access it.
bool HasValue(const wchar_t* value_name) const;
- // Returns the number of values for this key, of 0 if the number cannot be
+ // Returns the number of values for this key, or 0 if the number cannot be
// determined.
DWORD GetValueCount() const;
@@ -71,6 +71,10 @@ class BASE_EXPORT RegKey {
// it.
LONG DeleteKey(const wchar_t* name);
+ // Deletes an empty subkey. If the subkey has subkeys or values then this
+ // will fail.
+ LONG DeleteEmptyKey(const wchar_t* name);
+
// Deletes a single value within the key.
LONG DeleteValue(const wchar_t* name);
@@ -132,8 +136,20 @@ class BASE_EXPORT RegKey {
HKEY Handle() const { return key_; }
private:
+ // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to
+ // RegDeleteKey.
+ static LONG RegDeleteKeyExWrapper(HKEY hKey,
+ const wchar_t* lpSubKey,
+ REGSAM samDesired,
+ DWORD Reserved);
+
+ // Recursively deletes a key and all of its subkeys.
+ static LONG RegDelRecurse(HKEY root_key,
+ const std::wstring& name,
+ REGSAM access);
HKEY key_; // The registry key being iterated.
HANDLE watch_event_;
+ REGSAM wow64access_;
DISALLOW_COPY_AND_ASSIGN(RegKey);
};
diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc
index 155402a351f..84074b38586 100644
--- a/chromium/base/win/registry_unittest.cc
+++ b/chromium/base/win/registry_unittest.cc
@@ -9,6 +9,7 @@
#include "base/compiler_specific.h"
#include "base/stl_util.h"
+#include "base/win/windows_version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -16,31 +17,54 @@ namespace win {
namespace {
-const wchar_t kRootKey[] = L"Base_Registry_Unittest";
-
class RegistryTest : public testing::Test {
- public:
- RegistryTest() {}
-
protected:
+#if defined(_WIN64)
+ static const REGSAM kNativeViewMask = KEY_WOW64_64KEY;
+ static const REGSAM kRedirectedViewMask = KEY_WOW64_32KEY;
+#else
+ static const REGSAM kNativeViewMask = KEY_WOW64_32KEY;
+ static const REGSAM kRedirectedViewMask = KEY_WOW64_64KEY;
+#endif // _WIN64
+
+ RegistryTest() {}
virtual void SetUp() OVERRIDE {
// Create a temporary key.
RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS);
key.DeleteKey(kRootKey);
ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ));
+ foo_software_key_ = L"Software\\";
+ foo_software_key_ += kRootKey;
+ foo_software_key_ += L"\\Foo";
}
virtual void TearDown() OVERRIDE {
// Clean up the temporary key.
RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE);
ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+ ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
}
+ static bool IsRedirectorPresent() {
+#if defined(_WIN64)
+ return true;
+#else
+ return OSInfo::GetInstance()->wow64_status() == OSInfo::WOW64_ENABLED;
+#endif
+ }
+
+ const wchar_t* const kRootKey = L"Base_Registry_Unittest";
+ std::wstring foo_software_key_;
+
private:
DISALLOW_COPY_AND_ASSIGN(RegistryTest);
};
+// static
+const REGSAM RegistryTest::kNativeViewMask;
+const REGSAM RegistryTest::kRedirectedViewMask;
+
TEST_F(RegistryTest, ValueTest) {
RegKey key;
@@ -158,6 +182,173 @@ TEST_F(RegistryTest, TruncatedCharTest) {
EXPECT_FALSE(iterator.Valid());
}
+TEST_F(RegistryTest, RecursiveDelete) {
+ RegKey key;
+ // Create kRootKey->Foo
+ // \->Bar (TestValue)
+ // \->Foo (TestValue)
+ // \->Bar
+ // \->Foo
+ // \->Moo
+ // \->Foo
+ // and delete kRootKey->Foo
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
+ foo_key += L"\\Bar";
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData"));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
+
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar"));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo"));
+
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L""));
+ ASSERT_NE(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
+
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo"));
+ ASSERT_NE(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
+}
+
+// This test requires running as an Administrator as it tests redirected
+// registry writes to HKLM\Software
+// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx
+// TODO(wfh): flaky test on Vista. See http://crbug.com/377917
+TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) {
+ if (!IsRedirectorPresent())
+ return;
+
+ RegKey key;
+
+ // Test redirected key access from non-redirected.
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Create(HKEY_LOCAL_MACHINE,
+ foo_software_key_.c_str(),
+ KEY_WRITE | kRedirectedViewMask));
+ ASSERT_NE(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
+ ASSERT_NE(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ foo_software_key_.c_str(),
+ KEY_READ | kNativeViewMask));
+
+ // Open the non-redirected view of the parent and try to delete the test key.
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ L"Software",
+ KEY_SET_VALUE | kNativeViewMask));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+
+ // Open the redirected view and delete the key created above.
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ L"Software",
+ KEY_SET_VALUE | kRedirectedViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+}
+
+// Test for the issue found in http://crbug.com/384587 where OpenKey would call
+// Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a
+// subsequent OpenKey call.
+TEST_F(RegistryTest, SameWowFlags) {
+ RegKey key;
+
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ L"Software",
+ KEY_READ | KEY_WOW64_64KEY));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.OpenKey(L"Microsoft",
+ KEY_READ | KEY_WOW64_64KEY));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.OpenKey(L"Windows",
+ KEY_READ | KEY_WOW64_64KEY));
+}
+
+// TODO(wfh): flaky test on Vista. See http://crbug.com/377917
+TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) {
+ if (!IsRedirectorPresent())
+ return;
+ RegKey key;
+
+ // Test non-redirected key access from redirected.
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Create(HKEY_LOCAL_MACHINE,
+ foo_software_key_.c_str(),
+ KEY_WRITE | kNativeViewMask));
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ));
+ ASSERT_NE(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ foo_software_key_.c_str(),
+ KEY_READ | kRedirectedViewMask));
+
+ // Open the redirected view of the parent and try to delete the test key
+ // from the non-redirected view.
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ L"Software",
+ KEY_SET_VALUE | kRedirectedViewMask));
+ ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_LOCAL_MACHINE,
+ L"Software",
+ KEY_SET_VALUE | kNativeViewMask));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey));
+}
+
+TEST_F(RegistryTest, OpenSubKey) {
+ RegKey key;
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER,
+ kRootKey,
+ KEY_READ | KEY_CREATE_SUB_KEY));
+
+ ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ));
+ ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ));
+
+ std::wstring foo_key(kRootKey);
+ foo_key += L"\\Foo";
+ ASSERT_EQ(ERROR_SUCCESS,
+ key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ));
+
+ ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE));
+ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo"));
+}
+
} // namespace
} // namespace win
diff --git a/chromium/base/win/scoped_com_initializer.h b/chromium/base/win/scoped_com_initializer.h
index 392c351cc7d..92228baa507 100644
--- a/chromium/base/win/scoped_com_initializer.h
+++ b/chromium/base/win/scoped_com_initializer.h
@@ -16,6 +16,11 @@ namespace win {
// Initializes COM in the constructor (STA or MTA), and uninitializes COM in the
// destructor.
+//
+// WARNING: This should only be used once per thread, ideally scoped to a
+// similar lifetime as the thread itself. You should not be using this in
+// random utility functions that make COM calls -- instead ensure these
+// functions are running on a COM-supporting thread!
class ScopedCOMInitializer {
public:
// Enum value provided to initialize the thread as an MTA instead of STA.
diff --git a/chromium/base/win/scoped_gdi_object.h b/chromium/base/win/scoped_gdi_object.h
index d44310a1598..57b013e2fba 100644
--- a/chromium/base/win/scoped_gdi_object.h
+++ b/chromium/base/win/scoped_gdi_object.h
@@ -60,7 +60,7 @@ class ScopedGDIObject {
// An explicit specialization for HICON because we have to call DestroyIcon()
// instead of DeleteObject() for HICON.
template<>
-void ScopedGDIObject<HICON>::Close() {
+void inline ScopedGDIObject<HICON>::Close() {
if (object_)
DestroyIcon(object_);
}
diff --git a/chromium/base/win/scoped_handle.h b/chromium/base/win/scoped_handle.h
index 0d038e010bd..a85e08d26e5 100644
--- a/chromium/base/win/scoped_handle.h
+++ b/chromium/base/win/scoped_handle.h
@@ -27,11 +27,9 @@ namespace win {
// Generic wrapper for raw handles that takes care of closing handles
// automatically. The class interface follows the style of
-// the ScopedStdioHandle class with a few additions:
+// the ScopedFILE class with one addition:
// - IsValid() method can tolerate multiple invalid handle values such as NULL
// and INVALID_HANDLE_VALUE (-1) for Win32 handles.
-// - Receive() method allows to receive a handle value from a function that
-// takes a raw handle pointer only.
template <class Traits, class Verifier>
class GenericScopedHandle {
MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle, RValue)
@@ -168,7 +166,7 @@ class BASE_EXPORT VerifierTraits {
DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits);
};
-typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle;
+typedef GenericScopedHandle<HandleTraits, DummyVerifierTraits> ScopedHandle;
} // namespace win
} // namespace base
diff --git a/chromium/base/win/scoped_process_information_unittest.cc b/chromium/base/win/scoped_process_information_unittest.cc
index 550076ef9c6..0b720cb1a39 100644
--- a/chromium/base/win/scoped_process_information_unittest.cc
+++ b/chromium/base/win/scoped_process_information_unittest.cc
@@ -46,8 +46,7 @@ MULTIPROCESS_TEST_MAIN(ReturnNine) {
void ScopedProcessInformationTest::DoCreateProcess(
const std::string& main_id, PROCESS_INFORMATION* process_handle) {
- std::wstring cmd_line =
- this->MakeCmdLine(main_id, false).GetCommandLineString();
+ std::wstring cmd_line = MakeCmdLine(main_id).GetCommandLineString();
STARTUPINFO startup_info = {};
startup_info.cb = sizeof(startup_info);
diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc
index 57a93dc6524..7dace5988d0 100644
--- a/chromium/base/win/shortcut.cc
+++ b/chromium/base/win/shortcut.cc
@@ -11,6 +11,7 @@
#include "base/file_util.h"
#include "base/threading/thread_restrictions.h"
#include "base/win/scoped_comptr.h"
+#include "base/win/scoped_propvariant.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
@@ -172,52 +173,138 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path,
return succeeded;
}
-bool ResolveShortcut(const FilePath& shortcut_path,
- FilePath* target_path,
- string16* args) {
+bool ResolveShortcutProperties(const FilePath& shortcut_path,
+ uint32 options,
+ ShortcutProperties* properties) {
+ DCHECK(options && properties);
base::ThreadRestrictions::AssertIOAllowed();
- HRESULT result;
+ if (options & ~ShortcutProperties::PROPERTIES_ALL)
+ NOTREACHED() << "Unhandled property is used.";
+
ScopedComPtr<IShellLink> i_shell_link;
// Get pointer to the IShellLink interface.
- result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
- CLSCTX_INPROC_SERVER);
- if (FAILED(result))
+ if (FAILED(i_shell_link.CreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER))) {
return false;
+ }
ScopedComPtr<IPersistFile> persist;
// Query IShellLink for the IPersistFile interface.
- result = persist.QueryFrom(i_shell_link);
- if (FAILED(result))
+ if (FAILED(persist.QueryFrom(i_shell_link)))
return false;
// Load the shell link.
- result = persist->Load(shortcut_path.value().c_str(), STGM_READ);
- if (FAILED(result))
+ if (FAILED(persist->Load(shortcut_path.value().c_str(), STGM_READ)))
return false;
- WCHAR temp[MAX_PATH];
- if (target_path) {
+ // Reset |properties|.
+ properties->options = 0;
+
+ wchar_t temp[MAX_PATH];
+ if (options & ShortcutProperties::PROPERTIES_TARGET) {
// Try to find the target of a shortcut.
- result = i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH);
- if (FAILED(result))
+ if (FAILED(i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH)))
return false;
+ if (FAILED(i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY)))
+ return false;
+ properties->set_target(FilePath(temp));
+ }
- result = i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY);
- if (FAILED(result))
+ if (options & ShortcutProperties::PROPERTIES_WORKING_DIR) {
+ if (FAILED(i_shell_link->GetWorkingDirectory(temp, MAX_PATH)))
return false;
+ properties->set_working_dir(FilePath(temp));
+ }
- *target_path = FilePath(temp);
+ if (options & ShortcutProperties::PROPERTIES_ARGUMENTS) {
+ if (FAILED(i_shell_link->GetArguments(temp, MAX_PATH)))
+ return false;
+ properties->set_arguments(temp);
}
- if (args) {
- result = i_shell_link->GetArguments(temp, MAX_PATH);
- if (FAILED(result))
+ if (options & ShortcutProperties::PROPERTIES_DESCRIPTION) {
+ // Note: description length constrained by MAX_PATH.
+ if (FAILED(i_shell_link->GetDescription(temp, MAX_PATH)))
return false;
+ properties->set_description(temp);
+ }
- *args = string16(temp);
+ if (options & ShortcutProperties::PROPERTIES_ICON) {
+ int temp_index;
+ if (FAILED(i_shell_link->GetIconLocation(temp, MAX_PATH, &temp_index)))
+ return false;
+ properties->set_icon(FilePath(temp), temp_index);
}
+
+ // Windows 7+ options, avoiding unnecessary work.
+ if ((options & ShortcutProperties::PROPERTIES_WIN7) &&
+ GetVersion() >= VERSION_WIN7) {
+ ScopedComPtr<IPropertyStore> property_store;
+ if (FAILED(property_store.QueryFrom(i_shell_link)))
+ return false;
+
+ if (options & ShortcutProperties::PROPERTIES_APP_ID) {
+ ScopedPropVariant pv_app_id;
+ if (property_store->GetValue(PKEY_AppUserModel_ID,
+ pv_app_id.Receive()) != S_OK) {
+ return false;
+ }
+ switch (pv_app_id.get().vt) {
+ case VT_EMPTY:
+ properties->set_app_id(L"");
+ break;
+ case VT_LPWSTR:
+ properties->set_app_id(pv_app_id.get().pwszVal);
+ break;
+ default:
+ NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt;
+ return false;
+ }
+ }
+
+ if (options & ShortcutProperties::PROPERTIES_DUAL_MODE) {
+ ScopedPropVariant pv_dual_mode;
+ if (property_store->GetValue(PKEY_AppUserModel_IsDualMode,
+ pv_dual_mode.Receive()) != S_OK) {
+ return false;
+ }
+ switch (pv_dual_mode.get().vt) {
+ case VT_EMPTY:
+ properties->set_dual_mode(false);
+ break;
+ case VT_BOOL:
+ properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE);
+ break;
+ default:
+ NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt;
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+bool ResolveShortcut(const FilePath& shortcut_path,
+ FilePath* target_path,
+ string16* args) {
+ uint32 options = 0;
+ if (target_path)
+ options |= ShortcutProperties::PROPERTIES_TARGET;
+ if (args)
+ options |= ShortcutProperties::PROPERTIES_ARGUMENTS;
+ DCHECK(options);
+
+ ShortcutProperties properties;
+ if (!ResolveShortcutProperties(shortcut_path, options, &properties))
+ return false;
+
+ if (target_path)
+ *target_path = properties.target;
+ if (args)
+ *args = properties.arguments;
return true;
}
@@ -237,7 +324,7 @@ bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) {
base::ThreadRestrictions::AssertIOAllowed();
// "Unpin from taskbar" is only supported after Win7.
- if (base::win::GetVersion() < base::win::VERSION_WIN7)
+ if (GetVersion() < VERSION_WIN7)
return false;
int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin",
diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h
index 0f5fb0f686b..13940ed52c4 100644
--- a/chromium/base/win/shortcut.h
+++ b/chromium/base/win/shortcut.h
@@ -31,13 +31,21 @@ enum ShortcutOperation {
// setting |options| as desired.
struct ShortcutProperties {
enum IndividualProperties {
- PROPERTIES_TARGET = 1 << 0,
- PROPERTIES_WORKING_DIR = 1 << 1,
- PROPERTIES_ARGUMENTS = 1 << 2,
- PROPERTIES_DESCRIPTION = 1 << 3,
- PROPERTIES_ICON = 1 << 4,
- PROPERTIES_APP_ID = 1 << 5,
- PROPERTIES_DUAL_MODE = 1 << 6,
+ PROPERTIES_TARGET = 1U << 0,
+ PROPERTIES_WORKING_DIR = 1U << 1,
+ PROPERTIES_ARGUMENTS = 1U << 2,
+ PROPERTIES_DESCRIPTION = 1U << 3,
+ PROPERTIES_ICON = 1U << 4,
+ PROPERTIES_APP_ID = 1U << 5,
+ PROPERTIES_DUAL_MODE = 1U << 6,
+ // Be sure to update the values below when adding a new property.
+ PROPERTIES_BASIC = PROPERTIES_TARGET |
+ PROPERTIES_WORKING_DIR |
+ PROPERTIES_ARGUMENTS |
+ PROPERTIES_DESCRIPTION |
+ PROPERTIES_ICON,
+ PROPERTIES_WIN7 = PROPERTIES_APP_ID | PROPERTIES_DUAL_MODE,
+ PROPERTIES_ALL = PROPERTIES_BASIC | PROPERTIES_WIN7
};
ShortcutProperties() : icon_index(-1), dual_mode(false), options(0U) {}
@@ -117,14 +125,25 @@ BASE_EXPORT bool CreateOrUpdateShortcutLink(
const ShortcutProperties& properties,
ShortcutOperation operation);
-// Resolve Windows shortcut (.LNK file)
-// This methods tries to resolve a shortcut .LNK file. The path of the shortcut
-// to resolve is in |shortcut_path|. If |target_path| is not NULL, the target
-// will be resolved and placed in |target_path|. If |args| is not NULL, the
-// arguments will be retrieved and placed in |args|. The function returns true
-// if all requested fields are found successfully.
-// Callers can safely use the same variable for both |shortcut_path| and
-// |target_path|.
+// Resolves Windows shortcut (.LNK file)
+// This methods tries to resolve selected properties of a shortcut .LNK file.
+// The path of the shortcut to resolve is in |shortcut_path|. |options| is a bit
+// field composed of ShortcutProperties::IndividualProperties, to specify which
+// properties to read. It should be non-0. The resulting data are read into
+// |properties|, which must not be NULL. The function returns true if all
+// requested properties are successfully read. Otherwise some reads have failed
+// and intermediate values written to |properties| should be ignored.
+BASE_EXPORT bool ResolveShortcutProperties(const FilePath& shortcut_path,
+ uint32 options,
+ ShortcutProperties* properties);
+
+// Resolves Windows shortcut (.LNK file).
+// This is a wrapper to ResolveShortcutProperties() to handle the common use
+// case of resolving target and arguments. |target_path| and |args| are
+// optional output variables that are ignored if NULL (but at least one must be
+// non-NULL). The function returns true if all requested fields are found
+// successfully. Callers can safely use the same variable for both
+// |shortcut_path| and |target_path|.
BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path,
FilePath* target_path,
string16* args);
diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc
index eaf152eb9dc..7a6f41dae92 100644
--- a/chromium/base/win/shortcut_unittest.cc
+++ b/chromium/base/win/shortcut_unittest.cc
@@ -12,6 +12,7 @@
#include "base/test/test_file_util.h"
#include "base/test/test_shortcut_win.h"
#include "base/win/scoped_com_initializer.h"
+#include "base/win/windows_version.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -33,8 +34,7 @@ class ShortcutTest : public testing::Test {
// Shortcut 1's properties
{
const FilePath target_file(temp_dir_.path().Append(L"Target 1.txt"));
- file_util::WriteFile(target_file, kFileContents,
- arraysize(kFileContents));
+ WriteFile(target_file, kFileContents, arraysize(kFileContents));
link_properties_.set_target(target_file);
link_properties_.set_working_dir(temp_dir_.path());
@@ -48,11 +48,10 @@ class ShortcutTest : public testing::Test {
// Shortcut 2's properties (all different from properties of shortcut 1).
{
const FilePath target_file_2(temp_dir_.path().Append(L"Target 2.txt"));
- file_util::WriteFile(target_file_2, kFileContents2,
- arraysize(kFileContents2));
+ WriteFile(target_file_2, kFileContents2, arraysize(kFileContents2));
FilePath icon_path_2;
- base::CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2);
+ CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2);
link_properties_2_.set_target(target_file_2);
link_properties_2_.set_working_dir(temp_dir_2_.path());
@@ -80,6 +79,56 @@ class ShortcutTest : public testing::Test {
} // namespace
+TEST_F(ShortcutTest, CreateAndResolveShortcutProperties) {
+ uint32 valid_properties = ShortcutProperties::PROPERTIES_BASIC;
+ if (GetVersion() >= VERSION_WIN7)
+ valid_properties |= ShortcutProperties::PROPERTIES_WIN7;
+
+ // Test all properties.
+ FilePath file_1(temp_dir_.path().Append(L"Link1.lnk"));
+ ASSERT_TRUE(CreateOrUpdateShortcutLink(
+ file_1, link_properties_, SHORTCUT_CREATE_ALWAYS));
+
+ ShortcutProperties properties_read_1;
+ ASSERT_TRUE(ResolveShortcutProperties(
+ file_1, ShortcutProperties::PROPERTIES_ALL, &properties_read_1));
+ EXPECT_EQ(valid_properties, properties_read_1.options);
+ ValidatePathsAreEqual(link_properties_.target, properties_read_1.target);
+ ValidatePathsAreEqual(link_properties_.working_dir,
+ properties_read_1.working_dir);
+ EXPECT_EQ(link_properties_.arguments, properties_read_1.arguments);
+ EXPECT_EQ(link_properties_.description, properties_read_1.description);
+ ValidatePathsAreEqual(link_properties_.icon, properties_read_1.icon);
+ EXPECT_EQ(link_properties_.icon_index, properties_read_1.icon_index);
+ if (GetVersion() >= VERSION_WIN7) {
+ EXPECT_EQ(link_properties_.app_id, properties_read_1.app_id);
+ EXPECT_EQ(link_properties_.dual_mode, properties_read_1.dual_mode);
+ }
+
+ // Test simple shortcut with no special properties set.
+ FilePath file_2(temp_dir_.path().Append(L"Link2.lnk"));
+ ShortcutProperties only_target_properties;
+ only_target_properties.set_target(link_properties_.target);
+ ASSERT_TRUE(CreateOrUpdateShortcutLink(
+ file_2, only_target_properties, SHORTCUT_CREATE_ALWAYS));
+
+ ShortcutProperties properties_read_2;
+ ASSERT_TRUE(ResolveShortcutProperties(
+ file_2, ShortcutProperties::PROPERTIES_ALL, &properties_read_2));
+ EXPECT_EQ(valid_properties, properties_read_2.options);
+ ValidatePathsAreEqual(only_target_properties.target,
+ properties_read_2.target);
+ ValidatePathsAreEqual(FilePath(), properties_read_2.working_dir);
+ EXPECT_EQ(L"", properties_read_2.arguments);
+ EXPECT_EQ(L"", properties_read_2.description);
+ ValidatePathsAreEqual(FilePath(), properties_read_2.icon);
+ EXPECT_EQ(0, properties_read_2.icon_index);
+ if (GetVersion() >= VERSION_WIN7) {
+ EXPECT_EQ(L"", properties_read_2.app_id);
+ EXPECT_FALSE(properties_read_2.dual_mode);
+ }
+}
+
TEST_F(ShortcutTest, CreateAndResolveShortcut) {
ShortcutProperties only_target_properties;
only_target_properties.set_target(link_properties_.target);
diff --git a/chromium/base/win/startup_information_unittest.cc b/chromium/base/win/startup_information_unittest.cc
index d637ebd68a1..43276c5ee34 100644
--- a/chromium/base/win/startup_information_unittest.cc
+++ b/chromium/base/win/startup_information_unittest.cc
@@ -62,7 +62,7 @@ TEST_F(StartupInformationTest, InheritStdOut) {
sizeof(events[0])));
std::wstring cmd_line =
- this->MakeCmdLine("FireInheritedEvents", false).GetCommandLineString();
+ MakeCmdLine("FireInheritedEvents").GetCommandLineString();
PROCESS_INFORMATION temp_process_info = {};
ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(cmd_line.c_str()),
diff --git a/chromium/base/win/text_services_message_filter.cc b/chromium/base/win/text_services_message_filter.cc
deleted file mode 100644
index 7ce233d9fd4..00000000000
--- a/chromium/base/win/text_services_message_filter.cc
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/win/text_services_message_filter.h"
-
-namespace base {
-namespace win {
-
-TextServicesMessageFilter::TextServicesMessageFilter()
- : client_id_(TF_CLIENTID_NULL),
- is_initialized_(false) {
-}
-
-TextServicesMessageFilter::~TextServicesMessageFilter() {
- if (is_initialized_)
- thread_mgr_->Deactivate();
-}
-
-bool TextServicesMessageFilter::Init() {
- if (FAILED(thread_mgr_.CreateInstance(CLSID_TF_ThreadMgr)))
- return false;
-
- if (FAILED(message_pump_.QueryFrom(thread_mgr_)))
- return false;
-
- if (FAILED(keystroke_mgr_.QueryFrom(thread_mgr_)))
- return false;
-
- if (FAILED(thread_mgr_->Activate(&client_id_)))
- return false;
-
- is_initialized_ = true;
- return is_initialized_;
-}
-
-// Wraps for ITfMessagePump::PeekMessage with win32 PeekMessage signature.
-// Obtains messages from application message queue.
-BOOL TextServicesMessageFilter::DoPeekMessage(MSG* msg,
- HWND window_handle,
- UINT msg_filter_min,
- UINT msg_filter_max,
- UINT remove_msg) {
- BOOL result = FALSE;
- if (FAILED(message_pump_->PeekMessage(msg, window_handle,
- msg_filter_min, msg_filter_max,
- remove_msg, &result))) {
- result = FALSE;
- }
-
- return result;
-}
-
-// Sends message to Text Service Manager.
-// The message will be used to input composition text.
-// Returns true if |message| was consumed by text service manager.
-bool TextServicesMessageFilter::ProcessMessage(const MSG& msg) {
- if (msg.message == WM_KEYDOWN) {
- BOOL eaten = FALSE;
- HRESULT hr = keystroke_mgr_->TestKeyDown(msg.wParam, msg.lParam, &eaten);
- if (FAILED(hr) && !eaten)
- return false;
- eaten = FALSE;
- hr = keystroke_mgr_->KeyDown(msg.wParam, msg.lParam, &eaten);
- return (SUCCEEDED(hr) && !!eaten);
- }
-
- if (msg.message == WM_KEYUP) {
- BOOL eaten = FALSE;
- HRESULT hr = keystroke_mgr_->TestKeyUp(msg.wParam, msg.lParam, &eaten);
- if (FAILED(hr) && !eaten)
- return false;
- eaten = FALSE;
- hr = keystroke_mgr_->KeyUp(msg.wParam, msg.lParam, &eaten);
- return (SUCCEEDED(hr) && !!eaten);
- }
-
- return false;
-}
-
-} // namespace win
-} // namespace base
diff --git a/chromium/base/win/text_services_message_filter.h b/chromium/base/win/text_services_message_filter.h
deleted file mode 100644
index 704c1da640b..00000000000
--- a/chromium/base/win/text_services_message_filter.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
-#define BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
-
-#include <msctf.h>
-#include <Windows.h>
-
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_pump_win.h"
-#include "base/win/metro.h"
-#include "base/win/scoped_comptr.h"
-
-namespace base {
-namespace win {
-
-// TextServicesMessageFilter extends MessageFilter with methods that are using
-// Text Services Framework COM component.
-class BASE_EXPORT TextServicesMessageFilter
- : public base::MessagePumpForUI::MessageFilter {
- public:
- TextServicesMessageFilter();
- virtual ~TextServicesMessageFilter();
- virtual BOOL DoPeekMessage(MSG* msg,
- HWND window_handle,
- UINT msg_filter_min,
- UINT msg_filter_max,
- UINT remove_msg) OVERRIDE;
- virtual bool ProcessMessage(const MSG& msg) OVERRIDE;
-
- bool Init();
-
- private:
- TfClientId client_id_;
- bool is_initialized_;
- base::win::ScopedComPtr<ITfThreadMgr> thread_mgr_;
- base::win::ScopedComPtr<ITfMessagePump> message_pump_;
- base::win::ScopedComPtr<ITfKeystrokeMgr> keystroke_mgr_;
-
- DISALLOW_COPY_AND_ASSIGN(TextServicesMessageFilter);
-};
-
-} // namespace win
-} // namespace base
-
-#endif // BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_
diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc
index 7b85418a1ea..ee923522fc9 100644
--- a/chromium/base/win/win_util.cc
+++ b/chromium/base/win/win_util.cc
@@ -6,6 +6,7 @@
#include <aclapi.h>
#include <lm.h>
+#include <powrprof.h>
#include <shellapi.h>
#include <shlobj.h>
#include <shobjidl.h> // Must be before propkey.
@@ -221,14 +222,29 @@ void SetAbortBehaviorForCrashReporting() {
signal(SIGABRT, ForceCrashOnSigAbort);
}
-bool IsTouchEnabledDevice() {
- if (base::win::GetVersion() < base::win::VERSION_WIN7)
+bool IsTabletDevice() {
+ if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0)
return false;
- const int kMultiTouch = NID_INTEGRATED_TOUCH | NID_MULTI_INPUT | NID_READY;
- int sm = GetSystemMetrics(SM_DIGITIZER);
- if ((sm & kMultiTouch) == kMultiTouch) {
- return true;
- }
+
+ base::win::Version version = base::win::GetVersion();
+ if (version == base::win::VERSION_XP)
+ return (GetSystemMetrics(SM_TABLETPC) != 0);
+
+ // If the device is docked, the user is treating the device as a PC.
+ if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0)
+ return false;
+
+ // PlatformRoleSlate was only added in Windows 8, but prior to Win8 it is
+ // still possible to check for a mobile power profile.
+ POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole();
+ bool mobile_power_profile = (role == PlatformRoleMobile);
+ bool slate_power_profile = false;
+ if (version >= base::win::VERSION_WIN8)
+ slate_power_profile = (role == PlatformRoleSlate);
+
+ if (mobile_power_profile || slate_power_profile)
+ return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0);
+
return false;
}
@@ -329,41 +345,29 @@ bool DismissVirtualKeyboard() {
typedef HWND (*MetroRootWindow) ();
-// As of this writing, GetMonitorInfo function seem to return wrong values
-// for rcWork.left and rcWork.top in case of split screen situation inside
-// metro mode. In order to get required values we query for core window screen
-// coordinates.
-// TODO(shrikant): Remove detour code once GetMonitorInfo is fixed for 8.1.
-BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) {
- BOOL ret = ::GetMonitorInfo(monitor, mi);
-#if !defined(USE_ASH)
- if (base::win::IsMetroProcess() &&
- base::win::GetVersion() >= base::win::VERSION_WIN8_1) {
- static MetroRootWindow root_window = NULL;
- if (!root_window) {
- HMODULE metro = base::win::GetMetroModule();
- // There are apparently instances when current process is inside metro
- // environment but metro driver dll is not loaded.
- if (!metro) {
- return ret;
- }
- root_window = reinterpret_cast<MetroRootWindow>(
- ::GetProcAddress(metro, "GetRootWindow"));
- }
- ret = ::GetWindowRect(root_window(), &(mi->rcWork));
- }
-#endif
- return ret;
-}
+enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED};
+static volatile long int g_domain_state = UNKNOWN;
bool IsEnrolledToDomain() {
- LPWSTR domain;
- NETSETUP_JOIN_STATUS join_status;
- if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success)
- return false;
- ::NetApiBufferFree(domain);
+ // Doesn't make any sense to retry inside a user session because joining a
+ // domain will only kick in on a restart.
+ if (g_domain_state == UNKNOWN) {
+ LPWSTR domain;
+ NETSETUP_JOIN_STATUS join_status;
+ if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success)
+ return false;
+ ::NetApiBufferFree(domain);
+ ::InterlockedCompareExchange(&g_domain_state,
+ join_status == ::NetSetupDomainName ?
+ ENROLLED : NOT_ENROLLED,
+ UNKNOWN);
+ }
+
+ return g_domain_state == ENROLLED;
+}
- return join_status == ::NetSetupDomainName;
+void SetDomainStateForTesting(bool state) {
+ g_domain_state = state ? ENROLLED : NOT_ENROLLED;
}
} // namespace win
diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h
index eebb64ac8ef..573c4c72506 100644
--- a/chromium/base/win/win_util.h
+++ b/chromium/base/win/win_util.h
@@ -107,9 +107,10 @@ BASE_EXPORT bool ShouldCrashOnProcessDetach();
// process is aborted.
BASE_EXPORT void SetAbortBehaviorForCrashReporting();
-// A touch enabled device by this definition is something that has
-// integrated multi-touch ready to use and has Windows version > Windows7.
-BASE_EXPORT bool IsTouchEnabledDevice();
+// A tablet is a device that is touch enabled and also is being used
+// "like a tablet". This is used primarily for metrics in order to gain some
+// insight into how users use Chrome.
+BASE_EXPORT bool IsTabletDevice();
// Get the size of a struct up to and including the specified member.
// This is necessary to set compatible struct sizes for different versions
@@ -126,13 +127,13 @@ BASE_EXPORT bool DisplayVirtualKeyboard();
// above. Returns true on success.
BASE_EXPORT bool DismissVirtualKeyboard();
-// Returns monitor info after correcting rcWorkArea based on metro version.
-// see bug #247430 for more details.
-BASE_EXPORT BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi);
-
// Returns true if the machine is enrolled to a domain.
BASE_EXPORT bool IsEnrolledToDomain();
+// Used by tests to mock any wanted state. Call with |state| set to true to
+// simulate being in a domain and false otherwise.
+BASE_EXPORT void SetDomainStateForTesting(bool state);
+
} // namespace win
} // namespace base
diff --git a/chromium/base/x11/edid_parser_x11.cc b/chromium/base/x11/edid_parser_x11.cc
deleted file mode 100644
index 74b14b65c43..00000000000
--- a/chromium/base/x11/edid_parser_x11.cc
+++ /dev/null
@@ -1,196 +0,0 @@
-// 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.
-
-#include "base/x11/edid_parser_x11.h"
-
-#include <X11/extensions/Xrandr.h>
-#include <X11/Xatom.h>
-#include <X11/Xlib.h>
-
-#include "base/hash.h"
-#include "base/message_loop/message_loop.h"
-#include "base/strings/string_util.h"
-#include "base/sys_byteorder.h"
-
-namespace {
-
-// Returns 64-bit persistent ID for the specified manufacturer's ID and
-// product_code_hash, and the index of the output it is connected to.
-// |output_index| is used to distinguish the displays of the same type. For
-// example, swapping two identical display between two outputs will not be
-// treated as swap. The 'serial number' field in EDID isn't used here because
-// it is not guaranteed to have unique number and it may have the same fixed
-// value (like 0).
-int64 GetID(uint16 manufacturer_id,
- uint32 product_code_hash,
- uint8 output_index) {
- return ((static_cast<int64>(manufacturer_id) << 40) |
- (static_cast<int64>(product_code_hash) << 8) | output_index);
-}
-
-bool IsRandRAvailable() {
- int randr_version_major = 0;
- int randr_version_minor = 0;
- static bool is_randr_available = XRRQueryVersion(
- base::MessagePumpX11::GetDefaultXDisplay(),
- &randr_version_major, &randr_version_minor);
- return is_randr_available;
-}
-
-} // namespace
-
-namespace base {
-
-bool GetEDIDProperty(XID output, unsigned long* nitems, unsigned char** prop) {
- if (!IsRandRAvailable())
- return false;
-
- Display* display = base::MessagePumpX11::GetDefaultXDisplay();
-
- static Atom edid_property = XInternAtom(
- base::MessagePumpX11::GetDefaultXDisplay(),
- RR_PROPERTY_RANDR_EDID, false);
-
- bool has_edid_property = false;
- int num_properties = 0;
- Atom* properties = XRRListOutputProperties(display, output, &num_properties);
- for (int i = 0; i < num_properties; ++i) {
- if (properties[i] == edid_property) {
- has_edid_property = true;
- break;
- }
- }
- XFree(properties);
- if (!has_edid_property)
- return false;
-
- Atom actual_type;
- int actual_format;
- unsigned long bytes_after;
- XRRGetOutputProperty(display,
- output,
- edid_property,
- 0, // offset
- 128, // length
- false, // _delete
- false, // pending
- AnyPropertyType, // req_type
- &actual_type,
- &actual_format,
- nitems,
- &bytes_after,
- prop);
- DCHECK_EQ(XA_INTEGER, actual_type);
- DCHECK_EQ(8, actual_format);
- return true;
-}
-
-bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) {
- unsigned long nitems = 0;
- unsigned char* prop = NULL;
- if (!GetEDIDProperty(output_id, &nitems, &prop))
- return false;
-
- bool result =
- GetDisplayIdFromEDID(prop, nitems, output_index, display_id_out);
- XFree(prop);
- return result;
-}
-
-bool GetDisplayIdFromEDID(const unsigned char* prop,
- unsigned long nitems,
- size_t output_index,
- int64* display_id_out) {
- uint16 manufacturer_id = 0;
- std::string product_name;
-
- // ParseOutputDeviceData fails if it doesn't have product_name.
- ParseOutputDeviceData(prop, nitems, &manufacturer_id, &product_name);
-
- // Generates product specific value from product_name instead of product code.
- // See crbug.com/240341
- uint32 product_code_hash = product_name.empty() ?
- 0 : base::Hash(product_name);
- if (manufacturer_id != 0) {
- // An ID based on display's index will be assigned later if this call
- // fails.
- *display_id_out = GetID(
- manufacturer_id, product_code_hash, output_index);
- return true;
- }
- return false;
-}
-
-bool ParseOutputDeviceData(const unsigned char* prop,
- unsigned long nitems,
- uint16* manufacturer_id,
- std::string* human_readable_name) {
- // See http://en.wikipedia.org/wiki/Extended_display_identification_data
- // for the details of EDID data format. We use the following data:
- // bytes 8-9: manufacturer EISA ID, in big-endian
- // bytes 54-125: four descriptors (18-bytes each) which may contain
- // the display name.
- const unsigned int kManufacturerOffset = 8;
- const unsigned int kManufacturerLength = 2;
- const unsigned int kDescriptorOffset = 54;
- const unsigned int kNumDescriptors = 4;
- const unsigned int kDescriptorLength = 18;
- // The specifier types.
- const unsigned char kMonitorNameDescriptor = 0xfc;
-
- if (manufacturer_id) {
- if (nitems < kManufacturerOffset + kManufacturerLength) {
- LOG(ERROR) << "too short EDID data: manifacturer id";
- return false;
- }
-
- *manufacturer_id =
- *reinterpret_cast<const uint16*>(prop + kManufacturerOffset);
-#if defined(ARCH_CPU_LITTLE_ENDIAN)
- *manufacturer_id = base::ByteSwap(*manufacturer_id);
-#endif
- }
-
- if (!human_readable_name)
- return true;
-
- human_readable_name->clear();
- for (unsigned int i = 0; i < kNumDescriptors; ++i) {
- if (nitems < kDescriptorOffset + (i + 1) * kDescriptorLength)
- break;
-
- const unsigned char* desc_buf =
- prop + kDescriptorOffset + i * kDescriptorLength;
- // If the descriptor contains the display name, it has the following
- // structure:
- // bytes 0-2, 4: \0
- // byte 3: descriptor type, defined above.
- // bytes 5-17: text data, ending with \r, padding with spaces
- // we should check bytes 0-2 and 4, since it may have other values in
- // case that the descriptor contains other type of data.
- if (desc_buf[0] == 0 && desc_buf[1] == 0 && desc_buf[2] == 0 &&
- desc_buf[4] == 0) {
- if (desc_buf[3] == kMonitorNameDescriptor) {
- std::string found_name(
- reinterpret_cast<const char*>(desc_buf + 5), kDescriptorLength - 5);
- TrimWhitespaceASCII(found_name, TRIM_TRAILING, human_readable_name);
- break;
- }
- }
- }
-
- // Verify if the |human_readable_name| consists of printable characters only.
- for (size_t i = 0; i < human_readable_name->size(); ++i) {
- char c = (*human_readable_name)[i];
- if (!isascii(c) || !isprint(c)) {
- human_readable_name->clear();
- LOG(ERROR) << "invalid EDID: human unreadable char in name";
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace base
diff --git a/chromium/base/x11/edid_parser_x11.h b/chromium/base/x11/edid_parser_x11.h
deleted file mode 100644
index 0fba0b54d7b..00000000000
--- a/chromium/base/x11/edid_parser_x11.h
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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_X11_EDID_PARSER_X11_H_
-#define BASE_X11_EDID_PARSER_X11_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/basictypes.h"
-
-typedef unsigned long XID;
-
-// EDID (Extended Display Identification Data) is a format for monitor
-// metadata. This provides a parser for the data and an interface to get it
-// from XRandR.
-
-namespace base {
-
-// Get the EDID data from the |output| and stores to |prop|. |nitem| will store
-// the number of characters |prop| will have. It doesn't take the ownership of
-// |prop|, so caller must release it by XFree().
-// Returns true if EDID property is successfully obtained. Otherwise returns
-// false and does not touch |prop| and |nitems|.
-BASE_EXPORT bool GetEDIDProperty(XID output,
- unsigned long* nitems,
- unsigned char** prop);
-
-// Gets the EDID data from |output| and generates the display id through
-// |GetDisplayIdFromEDID|.
-BASE_EXPORT bool GetDisplayId(XID output, size_t index,
- int64* display_id_out);
-
-// Generates the display id for the pair of |prop| with |nitems| length and
-// |index|, and store in |display_id_out|. Returns true if the display id is
-// successfully generated, or false otherwise.
-BASE_EXPORT bool GetDisplayIdFromEDID(const unsigned char* prop,
- unsigned long nitems,
- size_t index,
- int64* display_id_out);
-
-// Parses |prop| as EDID data and stores extracted data into |manufacturer_id|
-// and |human_readable_name| and returns true. NULL can be passed for unwanted
-// output parameters. Some devices (especially internal displays) may not have
-// the field for |human_readable_name|, and it will return true in that case.
-BASE_EXPORT bool ParseOutputDeviceData(const unsigned char* prop,
- unsigned long nitems,
- uint16* manufacturer_id,
- std::string* human_readable_name);
-
-} // namespace base
-
-#endif // BASE_X11_EDID_PARSER_X11_H_
diff --git a/chromium/base/x11/edid_parser_x11_unittest.cc b/chromium/base/x11/edid_parser_x11_unittest.cc
deleted file mode 100644
index 97e3ce10340..00000000000
--- a/chromium/base/x11/edid_parser_x11_unittest.cc
+++ /dev/null
@@ -1,167 +0,0 @@
-// 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.
-
-#include "base/x11/edid_parser_x11.h"
-
-#include "base/memory/scoped_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include <X11/extensions/Xrandr.h>
-
-namespace base {
-
-namespace {
-
-// Returns the number of characters in the string literal but doesn't count its
-// terminator NULL byte.
-#define charsize(str) (arraysize(str) - 1)
-
-// Sample EDID data extracted from real devices.
-const unsigned char kNormalDisplay[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
- "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
- "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
- "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
- "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
- "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
- "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
- "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
-
-const unsigned char kInternalDisplay[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
- "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
- "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
- "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
- "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
- "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
- "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
-
-const unsigned char kOverscanDisplay[] =
- "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
- "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
- "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
- "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
- "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
- "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
- "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
- "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
- "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
- "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
- "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
- "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
- "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
- "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
- "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
-
-const unsigned char kLP2565A[] =
- "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
- "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
- "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00"
- "\xA9\x40\xB3\x00\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
- "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
- "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
- "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
- "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4";
-
-const unsigned char kLP2565B[] =
- "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01"
- "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
- "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F"
- "\xB3\x00\xD1\xC0\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
- "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
- "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
- "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
- "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45";
-
-} // namespace
-
-TEST(EdidParserX11Test, ParseEDID) {
- uint16 manufacturer_id = 0;
- std::string human_readable_name;
- EXPECT_TRUE(ParseOutputDeviceData(
- kNormalDisplay, charsize(kNormalDisplay),
- &manufacturer_id, &human_readable_name));
- EXPECT_EQ(0x22f0u, manufacturer_id);
- EXPECT_EQ("HP ZR30w", human_readable_name);
-
- manufacturer_id = 0;
- human_readable_name.clear();
- EXPECT_TRUE(ParseOutputDeviceData(
- kInternalDisplay, charsize(kInternalDisplay),
- &manufacturer_id, NULL));
- EXPECT_EQ(0x4ca3u, manufacturer_id);
- EXPECT_EQ("", human_readable_name);
-
- // Internal display doesn't have name.
- EXPECT_TRUE(ParseOutputDeviceData(
- kInternalDisplay, charsize(kInternalDisplay),
- NULL, &human_readable_name));
- EXPECT_TRUE(human_readable_name.empty());
-
- manufacturer_id = 0;
- human_readable_name.clear();
- EXPECT_TRUE(ParseOutputDeviceData(
- kOverscanDisplay, charsize(kOverscanDisplay),
- &manufacturer_id, &human_readable_name));
- EXPECT_EQ(0x4c2du, manufacturer_id);
- EXPECT_EQ("SAMSUNG", human_readable_name);
-}
-
-TEST(EdidParserX11Test, ParseBrokenEDID) {
- uint16 manufacturer_id = 0;
- std::string human_readable_name;
-
- // length == 0
- EXPECT_FALSE(ParseOutputDeviceData(
- kNormalDisplay, 0,
- &manufacturer_id, &human_readable_name));
-
- // name is broken. Copying kNormalDisplay and substitute its name data by
- // some control code.
- std::string display_data(
- reinterpret_cast<const char*>(kNormalDisplay), charsize(kNormalDisplay));
-
- // display's name data is embedded in byte 95-107 in this specific example.
- // Fix here too when the contents of kNormalDisplay is altered.
- display_data[97] = '\x1b';
- EXPECT_FALSE(ParseOutputDeviceData(
- reinterpret_cast<const unsigned char*>(display_data.data()),
- display_data.size(),
- &manufacturer_id, &human_readable_name));
-
- // If |human_readable_name| isn't specified, it skips parsing the name.
- manufacturer_id = 0;
- EXPECT_TRUE(ParseOutputDeviceData(
- reinterpret_cast<const unsigned char*>(display_data.data()),
- display_data.size(),
- &manufacturer_id, NULL));
- EXPECT_EQ(0x22f0u, manufacturer_id);
-}
-
-TEST(EdidParserX11Test, GetDisplayId) {
- // EDID of kLP2565A and B are slightly different but actually the same device.
- int64 id1 = -1;
- int64 id2 = -1;
- EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565A, charsize(kLP2565A), 0, &id1));
- EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565B, charsize(kLP2565B), 0, &id2));
- EXPECT_EQ(id1, id2);
- EXPECT_NE(-1, id1);
-}
-
-TEST(EdidParserX11Test, GetDisplayIdFromInternal) {
- int64 id = -1;
- EXPECT_TRUE(GetDisplayIdFromEDID(
- kInternalDisplay, charsize(kInternalDisplay), 0, &id));
- EXPECT_NE(-1, id);
-}
-
-TEST(EdidParserX11Test, GetDisplayIdFailure) {
- int64 id = -1;
- EXPECT_FALSE(GetDisplayIdFromEDID(NULL, 0, 0, &id));
- EXPECT_EQ(-1, id);
-}
-
-} // namespace base
diff --git a/chromium/base/x11/x11_error_tracker.cc b/chromium/base/x11/x11_error_tracker.cc
deleted file mode 100644
index 446408c5c3d..00000000000
--- a/chromium/base/x11/x11_error_tracker.cc
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 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.
-
-#include "base/x11/x11_error_tracker.h"
-
-#include "base/message_loop/message_pump_x11.h"
-
-namespace {
-
-unsigned char g_x11_error_code = 0;
-
-int X11ErrorHandler(Display* display, XErrorEvent* error) {
- g_x11_error_code = error->error_code;
- return 0;
-}
-
-}
-
-namespace base {
-
-X11ErrorTracker::X11ErrorTracker() {
- old_handler_ = XSetErrorHandler(X11ErrorHandler);
-}
-
-X11ErrorTracker::~X11ErrorTracker() {
- XSetErrorHandler(old_handler_);
-}
-
-bool X11ErrorTracker::FoundNewError() {
- XSync(MessagePumpForUI::GetDefaultXDisplay(), False);
- unsigned char error = g_x11_error_code;
- g_x11_error_code = 0;
- return error != 0;
-}
-
-} // namespace ui
diff --git a/chromium/base/x11/x11_error_tracker.h b/chromium/base/x11/x11_error_tracker.h
deleted file mode 100644
index 5542f52a056..00000000000
--- a/chromium/base/x11/x11_error_tracker.h
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 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_X11_X11_ERROR_TRACKER_H_
-#define BASE_X11_X11_ERROR_TRACKER_H_
-
-#include <X11/Xlib.h>
-
-#include "base/base_export.h"
-#include "base/basictypes.h"
-
-namespace base {
-
-// X11ErrorTracker catches X11 errors in a non-fatal way. It does so by
-// temporarily changing the X11 error handler. The old error handler is
-// restored when the tracker is destroyed.
-class BASE_EXPORT X11ErrorTracker {
- public:
- X11ErrorTracker();
- ~X11ErrorTracker();
-
- // Returns whether an X11 error happened since this function was last called
- // (or since the creation of the tracker). This is potentially expensive,
- // since this causes a sync with the X server.
- bool FoundNewError();
-
- private:
-#if !defined(TOOLKIT_GTK)
- XErrorHandler old_handler_;
-#endif
-
- DISALLOW_COPY_AND_ASSIGN(X11ErrorTracker);
-};
-
-} // namespace base
-
-#endif // BASE_X11_X11_ERROR_TRACKER_H_
diff --git a/chromium/base/x11/x11_error_tracker_gtk.cc b/chromium/base/x11/x11_error_tracker_gtk.cc
deleted file mode 100644
index 7a78a7c2750..00000000000
--- a/chromium/base/x11/x11_error_tracker_gtk.cc
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright 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.
-
-#include "base/x11/x11_error_tracker.h"
-
-#include <gdk/gdkx.h>
-
-#include "base/logging.h"
-
-namespace base {
-
-X11ErrorTracker::X11ErrorTracker() {
- gdk_error_trap_push();
-}
-
-X11ErrorTracker::~X11ErrorTracker() {
- gdk_error_trap_pop();
-}
-
-bool X11ErrorTracker::FoundNewError() {
- gdk_flush();
- bool found_error = gdk_error_trap_pop() != 0;
-
- gdk_error_trap_push();
- return found_error;
-}
-
-} // namespace base