summaryrefslogtreecommitdiff
path: root/chromium/base
diff options
context:
space:
mode:
authorAndras Becsi <andras.becsi@digia.com>2014-03-18 13:16:26 +0100
committerFrederik Gladhorn <frederik.gladhorn@digia.com>2014-03-20 15:55:39 +0100
commit3f0f86b0caed75241fa71c95a5d73bc0164348c5 (patch)
tree92b9fb00f2e9e90b0be2262093876d4f43b6cd13 /chromium/base
parente90d7c4b152c56919d963987e2503f9909a666d2 (diff)
downloadqtwebengine-chromium-3f0f86b0caed75241fa71c95a5d73bc0164348c5.tar.gz
Update to new stable branch 1750
This also includes an updated ninja and chromium dependencies needed on Windows. Change-Id: Icd597d80ed3fa4425933c9f1334c3c2e31291c42 Reviewed-by: Zoltan Arvai <zarvai@inf.u-szeged.hu> Reviewed-by: Zeno Albisser <zeno.albisser@digia.com>
Diffstat (limited to 'chromium/base')
-rw-r--r--chromium/base/OWNERS1
-rw-r--r--chromium/base/allocator/allocator.gyp41
-rw-r--r--chromium/base/allocator/allocator_shim.cc54
-rwxr-xr-xchromium/base/allocator/prep_libc.py7
-rw-r--r--chromium/base/allocator/win_allocator.cc5
-rw-r--r--chromium/base/android/OWNERS1
-rw-r--r--chromium/base/android/command_line_android.cc85
-rw-r--r--chromium/base/android/command_line_android.h26
-rw-r--r--chromium/base/android/content_uri_utils.cc39
-rw-r--r--chromium/base/android/content_uri_utils.h27
-rw-r--r--chromium/base/android/sys_utils.cc29
-rw-r--r--chromium/base/android/sys_utils.h7
-rw-r--r--chromium/base/android/sys_utils_unittest.cc23
-rw-r--r--chromium/base/base.gyp160
-rw-r--r--chromium/base/base.gypi53
-rw-r--r--chromium/base/base64.cc10
-rw-r--r--chromium/base/base64.h5
-rw-r--r--chromium/base/base64_unittest.cc3
-rw-r--r--chromium/base/base_paths.cc2
-rw-r--r--chromium/base/base_paths_android.cc2
-rw-r--r--chromium/base/base_paths_mac.mm2
-rw-r--r--chromium/base/base_paths_posix.cc4
-rw-r--r--chromium/base/base_switches.cc30
-rw-r--r--chromium/base/base_switches.h12
-rw-r--r--chromium/base/base_unittests.isolate8
-rw-r--r--chromium/base/basictypes.h18
-rw-r--r--chromium/base/callback.h9
-rw-r--r--chromium/base/callback_internal.h58
-rw-r--r--chromium/base/callback_list.h384
-rw-r--r--chromium/base/callback_list.h.pump (renamed from chromium/base/callback_registry.h)135
-rw-r--r--chromium/base/callback_list_unittest.cc291
-rw-r--r--chromium/base/callback_list_unittest.nc51
-rw-r--r--chromium/base/callback_registry_unittest.cc216
-rw-r--r--chromium/base/cancelable_callback.h4
-rw-r--r--chromium/base/chromeos/chromeos_version.cc24
-rw-r--r--chromium/base/chromeos/chromeos_version.h20
-rw-r--r--chromium/base/command_line.cc20
-rw-r--r--chromium/base/command_line.h11
-rw-r--r--chromium/base/compiler_specific.h30
-rw-r--r--chromium/base/containers/hash_tables.h2
-rw-r--r--chromium/base/containers/mru_cache.h9
-rw-r--r--chromium/base/cpu.cc65
-rw-r--r--chromium/base/cpu.h9
-rw-r--r--chromium/base/debug/crash_logging.cc11
-rw-r--r--chromium/base/debug/debugger_posix.cc32
-rw-r--r--chromium/base/debug/leak_annotations.h30
-rw-r--r--chromium/base/debug/profiler.cc6
-rw-r--r--chromium/base/debug/stack_trace_android.cc10
-rw-r--r--chromium/base/debug/stack_trace_posix.cc6
-rw-r--r--chromium/base/debug/trace_event.h384
-rw-r--r--chromium/base/debug/trace_event_android.cc156
-rw-r--r--chromium/base/debug/trace_event_impl.cc1610
-rw-r--r--chromium/base/debug/trace_event_impl.h383
-rw-r--r--chromium/base/debug/trace_event_memory.cc20
-rw-r--r--chromium/base/debug/trace_event_memory.h15
-rw-r--r--chromium/base/debug/trace_event_system_stats_monitor.cc7
-rw-r--r--chromium/base/debug/trace_event_unittest.cc834
-rw-r--r--chromium/base/debug/trace_event_win.cc15
-rw-r--r--chromium/base/debug/trace_event_win_unittest.cc2
-rw-r--r--chromium/base/event_recorder_win.cc12
-rw-r--r--chromium/base/file_util.cc54
-rw-r--r--chromium/base/file_util.h134
-rw-r--r--chromium/base/file_util_android.cc6
-rw-r--r--chromium/base/file_util_mac.mm7
-rw-r--r--chromium/base/file_util_posix.cc429
-rw-r--r--chromium/base/file_util_unittest.cc936
-rw-r--r--chromium/base/file_util_win.cc314
-rw-r--r--chromium/base/file_version_info.h30
-rw-r--r--chromium/base/files/dir_reader_linux.h2
-rw-r--r--chromium/base/files/file.cc64
-rw-r--r--chromium/base/files/file.h280
-rw-r--r--chromium/base/files/file_enumerator.h3
-rw-r--r--chromium/base/files/file_enumerator_win.cc14
-rw-r--r--chromium/base/files/file_path.cc58
-rw-r--r--chromium/base/files/file_path.h44
-rw-r--r--chromium/base/files/file_path_unittest.cc72
-rw-r--r--chromium/base/files/file_path_watcher_browsertest.cc114
-rw-r--r--chromium/base/files/file_path_watcher_kqueue.cc4
-rw-r--r--chromium/base/files/file_path_watcher_linux.cc34
-rw-r--r--chromium/base/files/file_path_watcher_win.cc29
-rw-r--r--chromium/base/files/file_posix.cc470
-rw-r--r--chromium/base/files/file_unittest.cc360
-rw-r--r--chromium/base/files/file_util_proxy.cc9
-rw-r--r--chromium/base/files/file_util_proxy_unittest.cc22
-rw-r--r--chromium/base/files/file_win.cc319
-rw-r--r--chromium/base/files/important_file_writer.cc8
-rw-r--r--chromium/base/files/memory_mapped_file_posix.cc3
-rw-r--r--chromium/base/files/scoped_temp_dir.cc14
-rw-r--r--chromium/base/files/scoped_temp_dir_unittest.cc10
-rw-r--r--chromium/base/guid.cc7
-rw-r--r--chromium/base/guid.h2
-rw-r--r--chromium/base/i18n/break_iterator.h6
-rw-r--r--chromium/base/i18n/case_conversion_unittest.cc2
-rw-r--r--chromium/base/i18n/file_util_icu.cc2
-rw-r--r--chromium/base/i18n/file_util_icu.h2
-rw-r--r--chromium/base/i18n/icu_util.cc41
-rw-r--r--chromium/base/i18n/time_formatting.cc5
-rw-r--r--chromium/base/i18n/timezone.cc608
-rw-r--r--chromium/base/i18n/timezone.h21
-rw-r--r--chromium/base/i18n/timezone_unittest.cc21
-rw-r--r--chromium/base/ios/device_util.h2
-rw-r--r--chromium/base/ios/device_util.mm4
-rw-r--r--chromium/base/ios/scoped_critical_action.h38
-rw-r--r--chromium/base/ios/scoped_critical_action.mm20
-rw-r--r--chromium/base/json/json_reader.cc4
-rw-r--r--chromium/base/json/json_reader.h1
-rw-r--r--chromium/base/json/json_string_value_serializer.h2
-rw-r--r--chromium/base/json/json_value_serializer_unittest.cc35
-rw-r--r--chromium/base/json/json_writer.cc32
-rw-r--r--chromium/base/json/json_writer.h21
-rw-r--r--chromium/base/json/string_escape.cc147
-rw-r--r--chromium/base/json/string_escape.h62
-rw-r--r--chromium/base/json/string_escape_unittest.cc184
-rw-r--r--chromium/base/linux_util.cc3
-rw-r--r--chromium/base/logging.h5
-rw-r--r--chromium/base/logging_unittest.cc7
-rw-r--r--chromium/base/mac/authorization_util.h17
-rw-r--r--chromium/base/mac/authorization_util.mm35
-rw-r--r--chromium/base/mac/foundation_util.h10
-rw-r--r--chromium/base/mac/foundation_util.mm60
-rw-r--r--chromium/base/mac/foundation_util_unittest.mm3
-rw-r--r--chromium/base/mac/mac_util.h36
-rw-r--r--chromium/base/mac/mac_util.mm60
-rw-r--r--chromium/base/mac/mac_util_unittest.mm46
-rw-r--r--chromium/base/mac/sdk_forward_declarations.h13
-rw-r--r--chromium/base/memory/aligned_memory.cc13
-rw-r--r--chromium/base/memory/discardable_memory.cc66
-rw-r--r--chromium/base/memory/discardable_memory.h61
-rw-r--r--chromium/base/memory/discardable_memory_allocator_android.cc418
-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.cc291
-rw-r--r--chromium/base/memory/discardable_memory_android.h37
-rw-r--r--chromium/base/memory/discardable_memory_emulated.cc65
-rw-r--r--chromium/base/memory/discardable_memory_emulated.h37
-rw-r--r--chromium/base/memory/discardable_memory_linux.cc35
-rw-r--r--chromium/base/memory/discardable_memory_mac.cc115
-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.cc90
-rw-r--r--chromium/base/memory/discardable_memory_win.cc35
-rw-r--r--chromium/base/memory/memory_pressure_listener.h7
-rw-r--r--chromium/base/memory/ref_counted.h4
-rw-r--r--chromium/base/memory/ref_counted_memory.cc20
-rw-r--r--chromium/base/memory/ref_counted_memory.h20
-rw-r--r--chromium/base/memory/ref_counted_memory_unittest.cc10
-rw-r--r--chromium/base/memory/ref_counted_unittest.cc22
-rw-r--r--chromium/base/memory/scoped_ptr.h6
-rw-r--r--chromium/base/memory/scoped_vector.h7
-rw-r--r--chromium/base/memory/scoped_vector_unittest.cc12
-rw-r--r--chromium/base/memory/shared_memory.h48
-rw-r--r--chromium/base/memory/shared_memory_android.cc9
-rw-r--r--chromium/base/memory/shared_memory_nacl.cc8
-rw-r--r--chromium/base/memory/shared_memory_posix.cc115
-rw-r--r--chromium/base/memory/shared_memory_unittest.cc109
-rw-r--r--chromium/base/memory/shared_memory_win.cc18
-rw-r--r--chromium/base/message_loop/message_loop.cc134
-rw-r--r--chromium/base/message_loop/message_loop.h22
-rw-r--r--chromium/base/message_loop/message_loop_test.cc1069
-rw-r--r--chromium/base/message_loop/message_loop_test.h137
-rw-r--r--chromium/base/message_loop/message_loop_unittest.cc1151
-rw-r--r--chromium/base/message_loop/message_pump_android.cc5
-rw-r--r--chromium/base/message_loop/message_pump_io_ios_unittest.cc4
-rw-r--r--chromium/base/message_loop/message_pump_libevent.cc4
-rw-r--r--chromium/base/message_loop/message_pump_libevent_unittest.cc4
-rw-r--r--chromium/base/message_loop/message_pump_win.cc24
-rw-r--r--chromium/base/message_loop/message_pump_win.h5
-rw-r--r--chromium/base/message_loop/message_pump_x11.cc12
-rw-r--r--chromium/base/message_loop/message_pump_x11.h3
-rw-r--r--chromium/base/metrics/field_trial.cc73
-rw-r--r--chromium/base/metrics/field_trial.h36
-rw-r--r--chromium/base/metrics/field_trial_unittest.cc43
-rw-r--r--chromium/base/metrics/histogram.h10
-rw-r--r--chromium/base/metrics/histogram_base.cc14
-rw-r--r--chromium/base/metrics/histogram_base.h5
-rw-r--r--chromium/base/metrics/histogram_base_unittest.cc34
-rw-r--r--chromium/base/metrics/histogram_delta_serialization.cc111
-rw-r--r--chromium/base/metrics/histogram_delta_serialization.h65
-rw-r--r--chromium/base/metrics/histogram_delta_serialization_unittest.cc54
-rw-r--r--chromium/base/metrics/histogram_snapshot_manager.cc2
-rw-r--r--chromium/base/metrics/statistics_recorder.cc32
-rw-r--r--chromium/base/metrics/statistics_recorder.h12
-rw-r--r--chromium/base/metrics/statistics_recorder_unittest.cc62
-rw-r--r--chromium/base/metrics/stats_table.cc168
-rw-r--r--chromium/base/metrics/stats_table.h10
-rw-r--r--chromium/base/move.h11
-rw-r--r--chromium/base/nix/mime_util_xdg.cc48
-rw-r--r--chromium/base/nix/xdg_util.cc4
-rw-r--r--chromium/base/observer_list.h17
-rw-r--r--chromium/base/os_compat_android_unittest.cc2
-rw-r--r--chromium/base/path_service.cc2
-rw-r--r--chromium/base/pickle.cc170
-rw-r--r--chromium/base/pickle.h135
-rw-r--r--chromium/base/pickle_unittest.cc52
-rw-r--r--chromium/base/platform_file.h49
-rw-r--r--chromium/base/platform_file_posix.cc54
-rw-r--r--chromium/base/platform_file_unittest.cc232
-rw-r--r--chromium/base/platform_file_win.cc32
-rw-r--r--chromium/base/posix/eintr_wrapper.h16
-rw-r--r--chromium/base/posix/file_descriptor_shuffle.cc2
-rw-r--r--chromium/base/posix/unix_domain_socket_linux_unittest.cc4
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source.h3
-rw-r--r--chromium/base/power_monitor/power_monitor_device_source_win.cc92
-rw-r--r--chromium/base/prefs/json_pref_store.cc36
-rw-r--r--chromium/base/prefs/json_pref_store.h3
-rw-r--r--chromium/base/prefs/json_pref_store_unittest.cc74
-rw-r--r--chromium/base/prefs/overlay_user_pref_store.cc5
-rw-r--r--chromium/base/prefs/overlay_user_pref_store.h1
-rw-r--r--chromium/base/prefs/persistent_pref_store.h4
-rw-r--r--chromium/base/prefs/pref_registry.cc8
-rw-r--r--chromium/base/prefs/pref_registry.h13
-rw-r--r--chromium/base/prefs/pref_service.cc68
-rw-r--r--chromium/base/prefs/pref_service.h41
-rw-r--r--chromium/base/prefs/pref_service_builder.cc110
-rw-r--r--chromium/base/prefs/pref_service_builder.h74
-rw-r--r--chromium/base/prefs/pref_service_factory.cc63
-rw-r--r--chromium/base/prefs/pref_service_factory.h91
-rw-r--r--chromium/base/prefs/scoped_user_pref_update.cc36
-rw-r--r--chromium/base/prefs/scoped_user_pref_update.h108
-rw-r--r--chromium/base/prefs/scoped_user_pref_update_unittest.cc81
-rw-r--r--chromium/base/prefs/testing_pref_service.h2
-rw-r--r--chromium/base/prefs/testing_pref_store.cc3
-rw-r--r--chromium/base/prefs/testing_pref_store.h1
-rw-r--r--chromium/base/process/internal_linux.h2
-rw-r--r--chromium/base/process/kill.h7
-rw-r--r--chromium/base/process/launch.cc1
-rw-r--r--chromium/base/process/launch.h31
-rw-r--r--chromium/base/process/launch_posix.cc4
-rw-r--r--chromium/base/process/launch_win.cc100
-rw-r--r--chromium/base/process/memory.h1
-rw-r--r--chromium/base/process/memory_linux.cc2
-rw-r--r--chromium/base/process/memory_mac.mm42
-rw-r--r--chromium/base/process/memory_unittest.cc13
-rw-r--r--chromium/base/process/process_handle_linux.cc2
-rw-r--r--chromium/base/process/process_metrics.cc8
-rw-r--r--chromium/base/process/process_metrics.h27
-rw-r--r--chromium/base/process/process_metrics_linux.cc203
-rw-r--r--chromium/base/process/process_metrics_unittest.cc397
-rw-r--r--chromium/base/process/process_metrics_unittest_ios.cc (renamed from chromium/base/process/process_util_unittest_ios.cc)7
-rw-r--r--chromium/base/process/process_metrics_unittests.cc63
-rw-r--r--chromium/base/process/process_util_unittest.cc216
-rw-r--r--chromium/base/rand_util.cc1
-rw-r--r--chromium/base/rand_util_posix.cc5
-rw-r--r--chromium/base/run_loop.cc8
-rw-r--r--chromium/base/run_loop.h6
-rw-r--r--chromium/base/scoped_native_library_unittest.cc18
-rw-r--r--chromium/base/security_unittest.cc30
-rw-r--r--chromium/base/sequence_checker.h2
-rw-r--r--chromium/base/strings/safe_sprintf.cc13
-rw-r--r--chromium/base/strings/safe_sprintf.h26
-rw-r--r--chromium/base/strings/safe_sprintf_unittest.cc12
-rw-r--r--chromium/base/strings/string16.cc2
-rw-r--r--chromium/base/strings/string16_unittest.cc4
-rw-r--r--chromium/base/strings/string_number_conversions.cc13
-rw-r--r--chromium/base/strings/string_number_conversions.h6
-rw-r--r--chromium/base/strings/string_number_conversions_unittest.cc60
-rw-r--r--chromium/base/strings/string_split.h6
-rw-r--r--chromium/base/strings/string_util.cc118
-rw-r--r--chromium/base/strings/string_util.h178
-rw-r--r--chromium/base/strings/string_util_constants.cc4
-rw-r--r--chromium/base/strings/string_util_unittest.cc82
-rw-r--r--chromium/base/supports_user_data.h2
-rw-r--r--chromium/base/sync_socket.h11
-rw-r--r--chromium/base/sync_socket_nacl.cc23
-rw-r--r--chromium/base/sync_socket_posix.cc158
-rw-r--r--chromium/base/sync_socket_unittest.cc131
-rw-r--r--chromium/base/sync_socket_win.cc108
-rw-r--r--chromium/base/synchronization/condition_variable_posix.cc64
-rw-r--r--chromium/base/synchronization/condition_variable_unittest.cc55
-rw-r--r--chromium/base/synchronization/waitable_event_posix.cc4
-rw-r--r--chromium/base/sys_info.h40
-rw-r--r--chromium/base/sys_info_chromeos.cc245
-rw-r--r--chromium/base/sys_info_internal.h34
-rw-r--r--chromium/base/sys_info_linux.cc61
-rw-r--r--chromium/base/sys_info_posix.cc20
-rw-r--r--chromium/base/sys_info_unittest.cc83
-rw-r--r--chromium/base/template_util.h42
-rw-r--r--chromium/base/template_util_unittest.cc50
-rw-r--r--chromium/base/third_party/dmg_fp/README.chromium2
-rw-r--r--chromium/base/third_party/dmg_fp/dtoa.cc6
-rw-r--r--chromium/base/third_party/dmg_fp/vs2013-optimization.patch18
-rw-r--r--chromium/base/third_party/icu/icu_utf.h7
-rw-r--r--chromium/base/threading/non_thread_safe.h2
-rw-r--r--chromium/base/threading/sequenced_worker_pool.h3
-rw-r--r--chromium/base/threading/thread.cc42
-rw-r--r--chromium/base/threading/thread.h21
-rw-r--r--chromium/base/threading/thread_checker.h2
-rw-r--r--chromium/base/threading/thread_local.h15
-rw-r--r--chromium/base/threading/thread_local_posix.cc12
-rw-r--r--chromium/base/threading/thread_local_win.cc14
-rw-r--r--chromium/base/threading/watchdog.cc38
-rw-r--r--chromium/base/threading/worker_pool_posix_unittest.cc1
-rw-r--r--chromium/base/time/time.cc24
-rw-r--r--chromium/base/time/time.h13
-rw-r--r--chromium/base/time/time_mac.cc39
-rw-r--r--chromium/base/time/time_posix.cc12
-rw-r--r--chromium/base/time/time_unittest.cc7
-rw-r--r--chromium/base/time/time_win.cc5
-rw-r--r--chromium/base/timer/elapsed_timer.cc17
-rw-r--r--chromium/base/timer/elapsed_timer.h30
-rw-r--r--chromium/base/timer/timer.cc21
-rw-r--r--chromium/base/timer/timer.h6
-rw-r--r--chromium/base/timer/timer_unittest.cc41
-rw-r--r--chromium/base/tracked_objects.cc31
-rw-r--r--chromium/base/values.cc29
-rw-r--r--chromium/base/values.h6
-rw-r--r--chromium/base/values_unittest.cc25
-rw-r--r--chromium/base/win/event_trace_controller_unittest.cc2
-rw-r--r--chromium/base/win/scoped_co_mem.h4
-rw-r--r--chromium/base/win/scoped_handle.h37
-rw-r--r--chromium/base/win/scoped_handle_unittest.cc31
-rw-r--r--chromium/base/win/scoped_process_information.cc20
-rw-r--r--chromium/base/win/scoped_process_information.h25
-rw-r--r--chromium/base/win/scoped_process_information_unittest.cc35
-rw-r--r--chromium/base/win/shortcut.h6
-rw-r--r--chromium/base/win/shortcut_unittest.cc8
-rw-r--r--chromium/base/win/startup_information_unittest.cc6
-rw-r--r--chromium/base/win/win_util.cc18
-rw-r--r--chromium/base/win/win_util.h3
-rw-r--r--chromium/base/win/windows_version.cc66
-rw-r--r--chromium/base/win/windows_version.h12
-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
328 files changed, 15859 insertions, 6998 deletions
diff --git a/chromium/base/OWNERS b/chromium/base/OWNERS
index f85ea53c41f..feb39cc1d8d 100644
--- a/chromium/base/OWNERS
+++ b/chromium/base/OWNERS
@@ -2,7 +2,6 @@ mark@chromium.org
darin@chromium.org
brettw@chromium.org
willchan@chromium.org
-jar@chromium.org
ajwong@chromium.org
thakis@chromium.org
diff --git a/chromium/base/allocator/allocator.gyp b/chromium/base/allocator/allocator.gyp
index ef98d09482b..4841f58c9a4 100644
--- a/chromium/base/allocator/allocator.gyp
+++ b/chromium/base/allocator/allocator.gyp
@@ -253,6 +253,8 @@
'<(tcmalloc_dir)/src/gperftools/profiler.h',
'<(tcmalloc_dir)/src/gperftools/stacktrace.h',
'<(tcmalloc_dir)/src/gperftools/tcmalloc.h',
+ '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
+ '<(tcmalloc_dir)/src/heap-checker.cc',
'<(tcmalloc_dir)/src/libc_override.h',
'<(tcmalloc_dir)/src/libc_override_gcc_and_weak.h',
'<(tcmalloc_dir)/src/libc_override_glibc.h',
@@ -325,6 +327,10 @@
],
},
},
+ # Disable the heap checker in tcmalloc.
+ 'defines': [
+ 'NO_HEAP_CHECK',
+ ],
'conditions': [
['OS=="linux" and clang_type_profiler==1', {
'dependencies': [
@@ -371,18 +377,9 @@
# included by allocator_shim.cc
'debugallocation_shim.cc',
- # heap-profiler/checker/cpuprofiler
+ # cpuprofiler
'<(tcmalloc_dir)/src/base/thread_lister.c',
'<(tcmalloc_dir)/src/base/thread_lister.h',
- '<(tcmalloc_dir)/src/deep-heap-profile.cc',
- '<(tcmalloc_dir)/src/deep-heap-profile.h',
- '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
- '<(tcmalloc_dir)/src/heap-checker.cc',
- '<(tcmalloc_dir)/src/heap-profiler.cc',
- '<(tcmalloc_dir)/src/heap-profile-table.cc',
- '<(tcmalloc_dir)/src/heap-profile-table.h',
- '<(tcmalloc_dir)/src/memory_region_map.cc',
- '<(tcmalloc_dir)/src/memory_region_map.h',
'<(tcmalloc_dir)/src/profiledata.cc',
'<(tcmalloc_dir)/src/profiledata.h',
'<(tcmalloc_dir)/src/profile-handler.cc',
@@ -439,30 +436,6 @@
'-fvtable-verify=preinit',
],
}],
- [ 'linux_keep_shadow_stacks==1', {
- 'sources': [
- '<(tcmalloc_dir)/src/linux_shadow_stacks.cc',
- '<(tcmalloc_dir)/src/linux_shadow_stacks.h',
- '<(tcmalloc_dir)/src/stacktrace_shadow-inl.h',
- ],
- 'cflags': [
- '-finstrument-functions',
- ],
- 'defines': [
- 'KEEP_SHADOW_STACKS',
- ],
- }],
- [ 'linux_use_heapchecker==0', {
- # Do not compile and link the heapchecker source.
- 'sources!': [
- '<(tcmalloc_dir)/src/heap-checker-bcad.cc',
- '<(tcmalloc_dir)/src/heap-checker.cc',
- ],
- # Disable the heap checker in tcmalloc.
- 'defines': [
- 'NO_HEAP_CHECK',
- ],
- }],
['order_profiling != 0', {
'target_conditions' : [
['_toolset=="target"', {
diff --git a/chromium/base/allocator/allocator_shim.cc b/chromium/base/allocator/allocator_shim.cc
index 1d8229117d7..9b3b50c4ee4 100644
--- a/chromium/base/allocator/allocator_shim.cc
+++ b/chromium/base/allocator/allocator_shim.cc
@@ -10,11 +10,9 @@
#include "base/sysinfo.h"
#include "jemalloc.h"
-// When defined, different heap allocators can be used via an environment
-// variable set before running the program. This may reduce the amount
-// of inlining that we get with malloc/free/etc. Disabling makes it
-// so that only tcmalloc can be used.
-#define ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
+// This shim make it possible to use different allocators via an environment
+// variable set before running the program. This may reduce the
+// amount of inlining that we get with malloc/free/etc.
// TODO(mbelshe): Ensure that all calls to tcmalloc have the proper call depth
// from the "user code" so that debugging tools (HeapChecker) can work.
@@ -119,7 +117,6 @@ extern "C" {
void* malloc(size_t size) __THROW {
void* ptr;
for (;;) {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
ptr = je_malloc(size);
@@ -133,10 +130,6 @@ void* malloc(size_t size) __THROW {
ptr = do_malloc(size);
break;
}
-#else
- // TCMalloc case.
- ptr = do_malloc(size);
-#endif
if (ptr)
return ptr;
@@ -147,7 +140,6 @@ void* malloc(size_t size) __THROW {
}
void free(void* p) __THROW {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
je_free(p);
@@ -156,10 +148,10 @@ void free(void* p) __THROW {
case WINLFH:
win_heap_free(p);
return;
+ case TCMALLOC:
+ do_free(p);
+ return;
}
-#endif
- // TCMalloc case.
- do_free(p);
}
void* realloc(void* ptr, size_t size) __THROW {
@@ -171,7 +163,6 @@ void* realloc(void* ptr, size_t size) __THROW {
void* new_ptr;
for (;;) {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
new_ptr = je_realloc(ptr, size);
@@ -185,10 +176,6 @@ void* realloc(void* ptr, size_t size) __THROW {
new_ptr = do_realloc(ptr, size);
break;
}
-#else
- // TCMalloc case.
- new_ptr = do_realloc(ptr, size);
-#endif
// Subtle warning: NULL return does not alwas indicate out-of-memory. If
// the requested new size is zero, realloc should free the ptr and return
@@ -203,7 +190,6 @@ void* realloc(void* ptr, size_t size) __THROW {
// TODO(mbelshe): Implement this for other allocators.
void malloc_stats(void) __THROW {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
// No stats.
@@ -212,15 +198,15 @@ void malloc_stats(void) __THROW {
case WINLFH:
// No stats.
return;
+ case TCMALLOC:
+ tc_malloc_stats();
+ return;
}
-#endif
- tc_malloc_stats();
}
#ifdef WIN32
extern "C" size_t _msize(void* p) {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
return je_msize(p);
@@ -228,7 +214,8 @@ extern "C" size_t _msize(void* p) {
case WINLFH:
return win_heap_msize(p);
}
-#endif
+
+ // TCMALLOC
return MallocExtension::instance()->GetAllocatedSize(p);
}
@@ -238,7 +225,6 @@ extern "C" intptr_t _get_heap_handle() {
}
static bool get_allocator_waste_size_thunk(size_t* size) {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
case WINHEAP:
@@ -246,7 +232,6 @@ static bool get_allocator_waste_size_thunk(size_t* size) {
// TODO(alexeif): Implement for allocators other than tcmalloc.
return false;
}
-#endif
size_t heap_size, allocated_bytes, unmapped_bytes;
MallocExtension* ext = MallocExtension::instance();
if (ext->GetNumericProperty("generic.heap_size", &heap_size) &&
@@ -270,7 +255,6 @@ static void release_free_memory_thunk() {
// The CRT heap initialization stub.
extern "C" int _heap_init() {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
// 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))
@@ -299,7 +283,7 @@ extern "C" int _heap_init() {
// fall through
break;
}
-#endif
+
// Initializing tcmalloc.
// We intentionally leak this object. It lasts for the process
// lifetime. Trying to teardown at _heap_term() is so late that
@@ -346,7 +330,6 @@ void* _aligned_malloc(size_t size, size_t alignment) {
void* ptr;
for (;;) {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
ptr = je_memalign(alignment, size);
@@ -360,10 +343,7 @@ void* _aligned_malloc(size_t size, size_t alignment) {
ptr = tc_memalign(alignment, size);
break;
}
-#else
- // TCMalloc case.
- ptr = tc_memalign(alignment, size);
-#endif
+
if (ptr) {
// Sanity check alignment.
DCHECK_EQ(reinterpret_cast<uintptr_t>(ptr) & (alignment - 1), 0U);
@@ -380,7 +360,6 @@ void _aligned_free(void* p) {
// Both JEMalloc and TCMalloc return pointers from memalign() that are safe to
// use with free(). Pointers allocated with win_heap_memalign() MUST be freed
// via win_heap_memalign_free() since the aligned pointer is not the real one.
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
switch (allocator) {
case JEMALLOC:
je_free(p);
@@ -389,10 +368,9 @@ void _aligned_free(void* p) {
case WINLFH:
win_heap_memalign_free(p);
return;
+ case TCMALLOC:
+ do_free(p);
}
-#endif
- // TCMalloc case.
- do_free(p);
}
#endif // WIN32
@@ -405,7 +383,6 @@ namespace base {
namespace allocator {
void SetupSubprocessAllocator() {
-#ifdef ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
size_t primary_length = 0;
getenv_s(&primary_length, NULL, 0, primary_name);
@@ -427,7 +404,6 @@ void SetupSubprocessAllocator() {
int ret_val = _putenv_s(primary_name, secondary_value);
DCHECK_EQ(0, ret_val);
}
-#endif // ENABLE_DYNAMIC_ALLOCATOR_SWITCHING
}
void* TCMallocDoMallocForTest(size_t size) {
diff --git a/chromium/base/allocator/prep_libc.py b/chromium/base/allocator/prep_libc.py
index e13e9e3b7e0..ba25cea092f 100755
--- a/chromium/base/allocator/prep_libc.py
+++ b/chromium/base/allocator/prep_libc.py
@@ -4,7 +4,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
-# This script takes libcmt.lib for VS2005/08/10 and removes the allocation
+# This script takes libcmt.lib for VS2005/08/10/12/13 and removes the allocation
# related functions from it.
#
# Usage: prep_libc.py <VCLibDir> <OutputDir> <arch>
@@ -51,7 +51,10 @@ def main():
'F:\\dd\\vctools\\crt_bld\\' + bindir + \
'\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativec\\\\',
'F:\\dd\\vctools\\crt_bld\\' + bindir + \
- '\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativecpp\\\\' ]
+ '\\crt\\src\\build\\' + objdir + '\\mt_obj\\nativecpp\\\\',
+ 'f:\\binaries\\Intermediate\\vctools\\crt_bld\\' + bindir + \
+ '\\crt\\prebuild\\build\\INTEL\\mt_obj\\cpp_obj\\\\',
+ ]
objfiles = ['malloc', 'free', 'realloc', 'new', 'delete', 'new2', 'delete2',
'align', 'msize', 'heapinit', 'expand', 'heapchk', 'heapwalk',
diff --git a/chromium/base/allocator/win_allocator.cc b/chromium/base/allocator/win_allocator.cc
index 899b867d11e..ee451f54610 100644
--- a/chromium/base/allocator/win_allocator.cc
+++ b/chromium/base/allocator/win_allocator.cc
@@ -56,7 +56,12 @@ void* win_heap_memalign(size_t alignment, size_t size) {
DCHECK_LT(size, allocation_size);
DCHECK_LT(alignment, allocation_size);
+ // Since we're directly calling the allocator function, before OOM handling,
+ // we need to NULL check to ensure the allocation succeeded.
void* ptr = win_heap_malloc(allocation_size);
+ if (!ptr)
+ return ptr;
+
char* aligned_ptr = static_cast<char*>(ptr) + sizeof(void*);
aligned_ptr +=
alignment - reinterpret_cast<uintptr_t>(aligned_ptr) & (alignment - 1);
diff --git a/chromium/base/android/OWNERS b/chromium/base/android/OWNERS
index ebc6e2624f0..87d5d224978 100644
--- a/chromium/base/android/OWNERS
+++ b/chromium/base/android/OWNERS
@@ -1,3 +1,2 @@
bulach@chromium.org
-joth@chromium.org
yfriedman@chromium.org
diff --git a/chromium/base/android/command_line_android.cc b/chromium/base/android/command_line_android.cc
new file mode 100644
index 00000000000..895ffab4f95
--- /dev/null
+++ b/chromium/base/android/command_line_android.cc
@@ -0,0 +1,85 @@
+// 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/command_line_android.h"
+
+#include "base/android/jni_array.h"
+#include "base/android/jni_string.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "jni/CommandLine_jni.h"
+
+using base::android::ConvertUTF8ToJavaString;
+using base::android::ConvertJavaStringToUTF8;
+
+namespace {
+
+void AppendJavaStringArrayToCommandLine(JNIEnv* env,
+ jobjectArray array,
+ bool includes_program) {
+ std::vector<std::string> vec;
+ if (array)
+ base::android::AppendJavaStringArrayToStringVector(env, array, &vec);
+ if (!includes_program)
+ vec.insert(vec.begin(), "");
+ CommandLine extra_command_line(vec);
+ CommandLine::ForCurrentProcess()->AppendArguments(extra_command_line,
+ includes_program);
+}
+
+} // namespace
+
+static void Reset(JNIEnv* env, jclass clazz) {
+ CommandLine::Reset();
+}
+
+static jboolean HasSwitch(JNIEnv* env, jclass clazz, jstring jswitch) {
+ std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+ return CommandLine::ForCurrentProcess()->HasSwitch(switch_string);
+}
+
+static jstring GetSwitchValue(JNIEnv* env, jclass clazz, jstring jswitch) {
+ std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+ std::string value(CommandLine::ForCurrentProcess()->GetSwitchValueNative(
+ switch_string));
+ if (value.empty())
+ return 0;
+ // OK to release, JNI binding.
+ return ConvertUTF8ToJavaString(env, value).Release();
+}
+
+static void AppendSwitch(JNIEnv* env, jclass clazz, jstring jswitch) {
+ std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+ CommandLine::ForCurrentProcess()->AppendSwitch(switch_string);
+}
+
+static void AppendSwitchWithValue(JNIEnv* env, jclass clazz,
+ jstring jswitch, jstring jvalue) {
+ std::string switch_string(ConvertJavaStringToUTF8(env, jswitch));
+ std::string value_string (ConvertJavaStringToUTF8(env, jvalue));
+ CommandLine::ForCurrentProcess()->AppendSwitchASCII(switch_string,
+ value_string);
+}
+
+static void AppendSwitchesAndArguments(JNIEnv* env, jclass clazz,
+ jobjectArray array) {
+ AppendJavaStringArrayToCommandLine(env, array, false);
+}
+
+namespace base {
+namespace android {
+
+void InitNativeCommandLineFromJavaArray(JNIEnv* env, jobjectArray array) {
+ // TODO(port): Make an overload of Init() that takes StringVector rather than
+ // have to round-trip via AppendArguments.
+ CommandLine::Init(0, NULL);
+ AppendJavaStringArrayToCommandLine(env, array, true);
+}
+
+bool RegisterCommandLine(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/android/command_line_android.h b/chromium/base/android/command_line_android.h
new file mode 100644
index 00000000000..cf117e2b4e5
--- /dev/null
+++ b/chromium/base/android/command_line_android.h
@@ -0,0 +1,26 @@
+// 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_COMMAND_LINE_ANDROID_H_
+#define BASE_ANDROID_COMMAND_LINE_ANDROID_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+
+namespace base {
+namespace android {
+
+// Appends all strings in the given array as flags to the Chrome command line.
+void BASE_EXPORT InitNativeCommandLineFromJavaArray(
+ JNIEnv* env,
+ jobjectArray init_command_line);
+
+// JNI registration boilerplate.
+bool RegisterCommandLine(JNIEnv* env);
+
+} // namespace android
+} // namespace base
+
+#endif // BASE_ANDROID_COMMAND_LINE_ANDROID_H_
diff --git a/chromium/base/android/content_uri_utils.cc b/chromium/base/android/content_uri_utils.cc
new file mode 100644
index 00000000000..64d6ad24226
--- /dev/null
+++ b/chromium/base/android/content_uri_utils.cc
@@ -0,0 +1,39 @@
+// 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/content_uri_utils.h"
+
+#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;
+
+namespace base {
+
+bool RegisterContentUriUtils(JNIEnv* env) {
+ return RegisterNativesImpl(env);
+}
+
+bool ContentUriExists(const FilePath& content_uri) {
+ JNIEnv* env = base::android::AttachCurrentThread();
+ ScopedJavaLocalRef<jstring> j_uri =
+ ConvertUTF8ToJavaString(env, content_uri.value());
+ return Java_ContentUriUtils_contentUriExists(
+ env, base::android::GetApplicationContext(), j_uri.obj());
+}
+
+int 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;
+}
+
+} // namespace base
diff --git a/chromium/base/android/content_uri_utils.h b/chromium/base/android/content_uri_utils.h
new file mode 100644
index 00000000000..ec820efbd26
--- /dev/null
+++ b/chromium/base/android/content_uri_utils.h
@@ -0,0 +1,27 @@
+// 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_CONTENT_URI_UTILS_H_
+#define BASE_ANDROID_CONTENT_URI_UTILS_H_
+
+#include <jni.h>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+
+namespace base {
+
+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);
+
+// Check whether a content uri exists.
+BASE_EXPORT bool ContentUriExists(const FilePath& content_uri);
+
+} // namespace base
+
+#endif // BASE_ANDROID_CONTENT_URI_UTILS_H_
diff --git a/chromium/base/android/sys_utils.cc b/chromium/base/android/sys_utils.cc
index bc18f81c721..efd13df3d42 100644
--- a/chromium/base/android/sys_utils.cc
+++ b/chromium/base/android/sys_utils.cc
@@ -8,16 +8,6 @@
#include "base/sys_info.h"
#include "jni/SysUtils_jni.h"
-const int64 kLowEndMemoryThreshold = 1024 * 1024 * 512; // 512 mb.
-
-// Only support low end device changes on builds greater than JB MR2.
-const int kLowEndSdkIntThreshold = 18;
-
-// Defined and called by JNI
-static jboolean IsLowEndDevice(JNIEnv* env, jclass clazz) {
- return base::android::SysUtils::IsLowEndDevice();
-}
-
namespace base {
namespace android {
@@ -25,9 +15,24 @@ bool SysUtils::Register(JNIEnv* env) {
return RegisterNativesImpl(env);
}
+bool SysUtils::IsLowEndDeviceFromJni() {
+ JNIEnv* env = AttachCurrentThread();
+ return Java_SysUtils_isLowEndDevice(env);
+}
+
bool SysUtils::IsLowEndDevice() {
- return SysInfo::AmountOfPhysicalMemory() <= kLowEndMemoryThreshold &&
- BuildInfo::GetInstance()->sdk_int() > kLowEndSdkIntThreshold;
+ static bool is_low_end = IsLowEndDeviceFromJni();
+ return is_low_end;
+}
+
+size_t SysUtils::AmountOfPhysicalMemoryKBFromJni() {
+ JNIEnv* env = AttachCurrentThread();
+ return static_cast<size_t>(Java_SysUtils_amountOfPhysicalMemoryKB(env));
+}
+
+size_t SysUtils::AmountOfPhysicalMemoryKB() {
+ static size_t amount_of_ram = AmountOfPhysicalMemoryKBFromJni();
+ return amount_of_ram;
}
SysUtils::SysUtils() { }
diff --git a/chromium/base/android/sys_utils.h b/chromium/base/android/sys_utils.h
index 78122ff572d..9b3152b466c 100644
--- a/chromium/base/android/sys_utils.h
+++ b/chromium/base/android/sys_utils.h
@@ -14,10 +14,17 @@ class BASE_EXPORT SysUtils {
public:
static bool Register(JNIEnv* env);
+ // Returns true iff this is a low-end device.
static bool IsLowEndDevice();
+ // Return the device's RAM size in kilo-bytes. Used for testing.
+ static size_t AmountOfPhysicalMemoryKB();
+
private:
SysUtils();
+
+ static bool IsLowEndDeviceFromJni();
+ static size_t AmountOfPhysicalMemoryKBFromJni();
};
} // namespace android
diff --git a/chromium/base/android/sys_utils_unittest.cc b/chromium/base/android/sys_utils_unittest.cc
new file mode 100644
index 00000000000..2a5a17d8532
--- /dev/null
+++ b/chromium/base/android/sys_utils_unittest.cc
@@ -0,0 +1,23 @@
+// 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/sys_utils.h"
+
+#include <unistd.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace android {
+
+TEST(SysUtils, AmountOfPhysicalMemory) {
+ // Check that the RAM size reported by sysconf() matches the one
+ // computed by base::android::SysUtils::AmountOfPhysicalMemory().
+ size_t sys_ram_size =
+ static_cast<size_t>(sysconf(_SC_PHYS_PAGES) * PAGE_SIZE);
+ EXPECT_EQ(sys_ram_size, SysUtils::AmountOfPhysicalMemoryKB() * 1024UL);
+}
+
+} // namespace android
+} // namespace base
diff --git a/chromium/base/base.gyp b/chromium/base/base.gyp
index 39f6015dba1..407d49d0582 100644
--- a/chromium/base/base.gyp
+++ b/chromium/base/base.gyp
@@ -35,7 +35,7 @@
],
},
'conditions': [
- ['use_glib==1', {
+ ['desktop_linux == 1 or chromeos == 1', {
'conditions': [
['chromeos==1', {
'sources/': [ ['include', '_chromeos\\.cc$'] ]
@@ -51,7 +51,6 @@
],
'dependencies': [
'symbolize',
- '../build/linux/system.gyp:glib',
'xdg_mime',
],
'defines': [
@@ -60,15 +59,20 @@
'cflags': [
'-Wno-write-strings',
],
- 'export_dependent_settings': [
- '../build/linux/system.gyp:glib',
- ],
- }, { # use_glib!=1
+ }, { # desktop_linux == 0 and chromeos == 0
'sources/': [
['exclude', '/xdg_user_dirs/'],
['exclude', '_nss\\.cc$'],
],
}],
+ ['use_glib==1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:glib',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:glib',
+ ],
+ }],
['use_x11==1', {
'dependencies': [
'../build/linux/system.gyp:x11',
@@ -77,6 +81,14 @@
'../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
@@ -335,6 +347,20 @@
'i18n/string_search.h',
'i18n/time_formatting.cc',
'i18n/time_formatting.h',
+ 'i18n/timezone.cc',
+ 'i18n/timezone.h',
+ ],
+ },
+ {
+ 'target_name': 'base_message_loop_tests',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'base',
+ '../testing/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'message_loop/message_loop_test.cc',
+ 'message_loop/message_loop_test.h',
],
},
{
@@ -376,14 +402,16 @@
'prefs/pref_registry_simple.h',
'prefs/pref_service.cc',
'prefs/pref_service.h',
- 'prefs/pref_service_builder.cc',
- 'prefs/pref_service_builder.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',
],
@@ -450,6 +478,7 @@
'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',
@@ -460,7 +489,8 @@
'bits_unittest.cc',
'build_time_unittest.cc',
'callback_helpers_unittest.cc',
- 'callback_registry_unittest.cc',
+ 'callback_list_unittest.cc',
+ 'callback_list_unittest.nc',
'callback_unittest.cc',
'callback_unittest.nc',
'cancelable_callback_unittest.cc',
@@ -486,6 +516,7 @@
'file_version_info_unittest.cc',
'files/dir_reader_posix_unittest.cc',
'files/file_path_unittest.cc',
+ 'files/file_unittest.cc',
'files/file_util_proxy_unittest.cc',
'files/important_file_writer_unittest.cc',
'files/scoped_temp_dir_unittest.cc',
@@ -501,6 +532,7 @@
'i18n/rtl_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',
@@ -520,7 +552,9 @@
'mac/scoped_sending_event_unittest.mm',
'md5_unittest.cc',
'memory/aligned_memory_unittest.cc',
+ 'memory/discardable_memory_allocator_android_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',
@@ -542,6 +576,7 @@
'metrics/bucket_ranges_unittest.cc',
'metrics/field_trial_unittest.cc',
'metrics/histogram_base_unittest.cc',
+ 'metrics/histogram_delta_serialization_unittest.cc',
'metrics/histogram_unittest.cc',
'metrics/sparse_histogram_unittest.cc',
'metrics/stats_table_unittest.cc',
@@ -564,12 +599,13 @@
'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_unittests.cc',
+ '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',
'safe_numerics_unittest.cc',
@@ -595,6 +631,7 @@
'strings/sys_string_conversions_unittest.cc',
'strings/utf_offset_string_conversions_unittest.cc',
'strings/utf_string_conversions_unittest.cc',
+ 'sync_socket_unittest.cc',
'synchronization/cancellation_flag_unittest.cc',
'synchronization/condition_variable_unittest.cc',
'synchronization/lock_unittest.cc',
@@ -606,6 +643,7 @@
'template_util_unittest.cc',
'test/expectations/expectation_unittest.cc',
'test/expectations/parser_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',
@@ -644,7 +682,6 @@
'win/registry_unittest.cc',
'win/scoped_bstr_unittest.cc',
'win/scoped_comptr_unittest.cc',
- 'win/scoped_handle_unittest.cc',
'win/scoped_process_information_unittest.cc',
'win/scoped_variant_unittest.cc',
'win/shortcut_unittest.cc',
@@ -655,6 +692,7 @@
'dependencies': [
'base',
'base_i18n',
+ 'base_message_loop_tests',
'base_prefs',
'base_prefs_test_support',
'base_static',
@@ -672,7 +710,7 @@
'module_dir': 'base'
},
'conditions': [
- ['use_glib==1', {
+ ['desktop_linux == 1 or chromeos == 1', {
'defines': [
'USE_SYMBOLIZE',
],
@@ -700,26 +738,6 @@
# iOS does not use message_pump_libevent.
['exclude', '^message_loop/message_pump_libevent_unittest\\.cc$'],
],
- 'conditions': [
- ['coverage != 0', {
- 'sources!': [
- # These sources can't be built with coverage due to a toolchain
- # bug: http://openradar.appspot.com/radar?id=1499403
- 'json/json_reader_unittest.cc',
- 'strings/string_piece_unittest.cc',
-
- # These tests crash when run with coverage turned on due to an
- # issue with llvm_gcda_increment_indirect_counter:
- # http://crbug.com/156058
- 'debug/trace_event_unittest.cc',
- 'debug/trace_event_unittest.h',
- 'logging_unittest.cc',
- 'string_util_unittest.cc',
- 'test/trace_event_analyzer_unittest.cc',
- 'utf_offset_string_conversions_unittest.cc',
- ],
- }],
- ],
'actions': [
{
'action_name': 'copy_test_data',
@@ -733,7 +751,7 @@
},
],
}],
- ['use_glib==1', {
+ ['desktop_linux == 1 or chromeos == 1', {
'sources!': [
'file_version_info_unittest.cc',
],
@@ -748,11 +766,19 @@
}],
],
'dependencies': [
- '../build/linux/system.gyp:glib',
'../build/linux/system.gyp:ssl',
+ ],
+ }],
+ ['use_x11 == 1', {
+ 'dependencies': [
'../tools/xdisplaycheck/xdisplaycheck.gyp:xdisplaycheck',
],
- }, { # use_glib!=1
+ }],
+ ['use_glib == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:glib',
+ ],
+ }, { # use_glib == 0
'sources!': [
'message_loop/message_pump_glib_unittest.cc',
]
@@ -804,11 +830,14 @@
['exclude', '^win/'],
],
'sources!': [
- 'debug/trace_event_win_unittest.cc',
- 'time/time_win_unittest.cc',
'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',
@@ -866,6 +895,15 @@
'test/test_file_util_linux.cc',
],
}],
+ ['OS == "android"', {
+ 'dependencies': [
+ 'base_unittests_jni_headers',
+ 'base_java_unittest_support',
+ ],
+ 'include_dirs': [
+ '<(SHARED_INTERMEDIATE_DIR)/base',
+ ],
+ }],
],
'sources': [
'test/expectations/expectation.cc',
@@ -874,6 +912,15 @@
'test/expectations/parser.h',
'test/gtest_xml_util.cc',
'test/gtest_xml_util.h',
+ 'test/launcher/test_launcher.cc',
+ 'test/launcher/test_launcher.h',
+ 'test/launcher/test_result.cc',
+ 'test/launcher/test_result.h',
+ 'test/launcher/test_results_tracker.cc',
+ 'test/launcher/test_results_tracker.h',
+ 'test/launcher/unit_test_launcher.cc',
+ 'test/launcher/unit_test_launcher.h',
+ 'test/launcher/unit_test_launcher_ios.cc',
'test/mock_chrome_application_mac.h',
'test/mock_chrome_application_mac.mm',
'test/mock_devices_changed_observer.cc',
@@ -885,16 +932,12 @@
'test/multiprocess_test_android.cc',
'test/null_task_runner.cc',
'test/null_task_runner.h',
- 'test/parallel_test_launcher.cc',
- 'test/parallel_test_launcher.h',
'test/perf_log.cc',
'test/perf_log.h',
'test/perf_test_suite.cc',
'test/perf_test_suite.h',
'test/perf_time_logger.cc',
'test/perf_time_logger.h',
- 'test/perftimer.cc',
- 'test/perftimer.h',
'test/power_monitor_test_base.cc',
'test/power_monitor_test_base.h',
'test/scoped_locale.cc',
@@ -913,12 +956,11 @@
'test/task_runner_test_template.h',
'test/test_file_util.cc',
'test/test_file_util.h',
+ 'test/test_file_util_android.cc',
'test/test_file_util_linux.cc',
'test/test_file_util_mac.cc',
'test/test_file_util_posix.cc',
'test/test_file_util_win.cc',
- 'test/test_launcher.cc',
- 'test/test_launcher.h',
'test/test_listener_ios.h',
'test/test_listener_ios.mm',
'test/test_pending_task.cc',
@@ -945,9 +987,6 @@
'test/thread_test_helper.h',
'test/trace_event_analyzer.cc',
'test/trace_event_analyzer.h',
- 'test/unit_test_launcher.cc',
- 'test/unit_test_launcher.h',
- 'test/unit_test_launcher_ios.cc',
'test/values_test_util.cc',
'test/values_test_util.h',
],
@@ -960,7 +999,7 @@
],
'sources!': [
# iOS uses its own unit test launcher.
- 'test/unit_test_launcher.cc',
+ 'test/launcher/unit_test_launcher.cc',
],
}],
], # target_conditions
@@ -1184,6 +1223,8 @@
'sources': [
'android/java/src/org/chromium/base/ActivityStatus.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/ImportantFileWriterAndroid.java',
'android/java/src/org/chromium/base/MemoryPressureListener.java',
@@ -1204,6 +1245,18 @@
],
'variables': {
'jni_gen_package': 'base',
+ 'jni_generator_ptr_type': 'long',
+ },
+ 'includes': [ '../build/jni_generator.gypi' ],
+ },
+ {
+ 'target_name': 'base_unittests_jni_headers',
+ 'type': 'none',
+ 'sources': [
+ 'test/android/java/src/org/chromium/base/ContentUriTestUtils.java',
+ ],
+ 'variables': {
+ 'jni_gen_package': 'base',
},
'includes': [ '../build/jni_generator.gypi' ],
},
@@ -1227,6 +1280,17 @@
],
},
{
+ 'target_name': 'base_java_unittest_support',
+ 'type': 'none',
+ 'dependencies': [
+ 'base_java',
+ ],
+ 'variables': {
+ 'java_in_dir': '../base/test/android/java',
+ },
+ 'includes': [ '../build/java.gypi' ],
+ },
+ {
'target_name': 'base_java_activity_state',
'type': 'none',
# This target is used to auto-generate ActivityState.java
diff --git a/chromium/base/base.gypi b/chromium/base/base.gypi
index 8caee68d5ee..11edab00968 100644
--- a/chromium/base/base.gypi
+++ b/chromium/base/base.gypi
@@ -39,6 +39,10 @@
'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/fifo_utils.cc',
'android/fifo_utils.h',
@@ -108,10 +112,8 @@
'callback_helpers.h',
'callback_internal.cc',
'callback_internal.h',
- 'callback_registry.h',
+ 'callback_list.h',
'cancelable_callback.h',
- 'chromeos/chromeos_version.cc',
- 'chromeos/chromeos_version.h',
'command_line.cc',
'command_line.h',
'compiler_specific.h',
@@ -179,6 +181,8 @@
'files/dir_reader_fallback.h',
'files/dir_reader_linux.h',
'files/dir_reader_posix.h',
+ 'files/file.cc',
+ 'files/file.h',
'files/file_enumerator.cc',
'files/file_enumerator.h',
'files/file_enumerator_posix.cc',
@@ -192,8 +196,10 @@
'files/file_path_watcher_linux.cc',
'files/file_path_watcher_stub.cc',
'files/file_path_watcher_win.cc',
+ 'files/file_posix.cc',
'files/file_util_proxy.cc',
'files/file_util_proxy.h',
+ 'files/file_win.cc',
'files/important_file_writer.h',
'files/important_file_writer.cc',
'files/memory_mapped_file.cc',
@@ -285,10 +291,18 @@
'mac/sdk_forward_declarations.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_win.cc',
'memory/linked_ptr.h',
'memory/manual_constructor.h',
'memory/memory_pressure_listener.cc',
@@ -341,6 +355,8 @@
'metrics/histogram.h',
'metrics/histogram_base.cc',
'metrics/histogram_base.h',
+ 'metrics/histogram_delta_serialization.cc',
+ 'metrics/histogram_delta_serialization.h',
'metrics/histogram_flattener.h',
'metrics/histogram_samples.cc',
'metrics/histogram_samples.h',
@@ -534,6 +550,7 @@
'sys_info_android.cc',
'sys_info_chromeos.cc',
'sys_info_freebsd.cc',
+ 'sys_info_internal.h',
'sys_info_ios.mm',
'sys_info_linux.cc',
'sys_info_mac.cc',
@@ -598,9 +615,11 @@
'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/hi_res_timer_manager.h',
'timer/timer.cc',
'timer/timer.h',
'tracked_objects.cc',
@@ -667,8 +686,17 @@
'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',
@@ -686,13 +714,21 @@
4018,
],
'target_conditions': [
- ['<(use_glib)==0 or >(nacl_untrusted_build)==1', {
+ ['(<(desktop_linux) == 0 and <(chromeos) == 0) or >(nacl_untrusted_build)==1', {
'sources/': [
['exclude', '^nix/'],
],
'sources!': [
'atomicops_internals_x86_gcc.cc',
+ ],
+ }],
+ ['<(use_glib)==0 or >(nacl_untrusted_build)==1', {
+ 'sources!': [
'message_loop/message_pump_glib.cc',
+ ],
+ }],
+ ['<(use_x11)==0 or >(nacl_untrusted_build)==1', {
+ 'sources!': [
'message_loop/message_pump_x11.cc',
],
}],
@@ -903,6 +939,11 @@
['exclude', '^third_party/nspr/'],
],
}],
+ ['<(toolkit_uses_gtk) == 1', {
+ 'sources!': [
+ 'x11/x11_error_tracker.cc',
+ ],
+ }],
],
}],
],
diff --git a/chromium/base/base64.cc b/chromium/base/base64.cc
index 9514b0a5c2c..8ed1249257d 100644
--- a/chromium/base/base64.cc
+++ b/chromium/base/base64.cc
@@ -8,21 +8,15 @@
namespace base {
-bool Base64Encode(const StringPiece& input, std::string* output) {
+void Base64Encode(const StringPiece& input, std::string* output) {
std::string temp;
temp.resize(modp_b64_encode_len(input.size())); // makes room for null byte
- // null terminates result since result is base64 text!
- int input_size = static_cast<int>(input.size());
-
// modp_b64_encode_len() returns at least 1, so temp[0] is safe to use.
- size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input_size);
- if (output_size == MODP_B64_ERROR)
- return false;
+ size_t output_size = modp_b64_encode(&(temp[0]), input.data(), input.size());
temp.resize(output_size); // strips off null byte
output->swap(temp);
- return true;
}
bool Base64Decode(const StringPiece& input, std::string* output) {
diff --git a/chromium/base/base64.h b/chromium/base/base64.h
index cc78edce5c6..43d8f76eb76 100644
--- a/chromium/base/base64.h
+++ b/chromium/base/base64.h
@@ -12,9 +12,8 @@
namespace base {
-// Encodes the input string in base64. Returns true if successful and false
-// otherwise. The output string is only modified if successful.
-BASE_EXPORT bool Base64Encode(const StringPiece& input, std::string* output);
+// Encodes the input string in base64.
+BASE_EXPORT void Base64Encode(const StringPiece& input, std::string* output);
// Decodes the base64 input string. Returns true if successful and false
// otherwise. The output string is only modified if successful.
diff --git a/chromium/base/base64_unittest.cc b/chromium/base/base64_unittest.cc
index 9a5dd818e06..9b23194ccf6 100644
--- a/chromium/base/base64_unittest.cc
+++ b/chromium/base/base64_unittest.cc
@@ -16,8 +16,7 @@ TEST(Base64Test, Basic) {
std::string decoded;
bool ok;
- ok = Base64Encode(kText, &encoded);
- EXPECT_TRUE(ok);
+ Base64Encode(kText, &encoded);
EXPECT_EQ(kBase64Text, encoded);
ok = Base64Decode(encoded, &decoded);
diff --git a/chromium/base/base_paths.cc b/chromium/base/base_paths.cc
index 9f2b250561f..b4fc28b6c75 100644
--- a/chromium/base/base_paths.cc
+++ b/chromium/base/base_paths.cc
@@ -24,7 +24,7 @@ bool PathProvider(int key, FilePath* result) {
cur = cur.DirName();
break;
case DIR_TEMP:
- if (!file_util::GetTempDir(&cur))
+ if (!base::GetTempDir(&cur))
return false;
break;
case DIR_TEST_DATA:
diff --git a/chromium/base/base_paths_android.cc b/chromium/base/base_paths_android.cc
index d0a709fc3a8..68cc5a72cef 100644
--- a/chromium/base/base_paths_android.cc
+++ b/chromium/base/base_paths_android.cc
@@ -48,7 +48,7 @@ bool PathProviderAndroid(int key, FilePath* result) {
case base::DIR_ANDROID_APP_DATA:
return base::android::GetDataDirectory(result);
case base::DIR_HOME:
- *result = file_util::GetHomeDir();
+ *result = GetHomeDir();
return true;
case base::DIR_ANDROID_EXTERNAL_STORAGE:
return base::android::GetExternalStorageDirectory(result);
diff --git a/chromium/base/base_paths_mac.mm b/chromium/base/base_paths_mac.mm
index 5d4461cb236..86d6a80e9ad 100644
--- a/chromium/base/base_paths_mac.mm
+++ b/chromium/base/base_paths_mac.mm
@@ -71,7 +71,7 @@ bool PathProviderMac(int key, base::FilePath* result) {
#if defined(OS_IOS)
// On IOS, this directory does not exist unless it is created explicitly.
if (success && !base::PathExists(*result))
- success = file_util::CreateDirectory(*result);
+ success = base::CreateDirectory(*result);
#endif // defined(OS_IOS)
return success;
}
diff --git a/chromium/base/base_paths_posix.cc b/chromium/base/base_paths_posix.cc
index 2d5fcbd26d6..b4147e99327 100644
--- a/chromium/base/base_paths_posix.cc
+++ b/chromium/base/base_paths_posix.cc
@@ -36,7 +36,7 @@ bool PathProviderPosix(int key, FilePath* result) {
case base::FILE_MODULE: { // TODO(evanm): is this correct?
#if defined(OS_LINUX)
FilePath bin_dir;
- if (!file_util::ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) {
+ if (!ReadSymbolicLink(FilePath(kProcSelfExe), &bin_dir)) {
NOTREACHED() << "Unable to resolve " << kProcSelfExe << ".";
return false;
}
@@ -110,7 +110,7 @@ bool PathProviderPosix(int key, FilePath* result) {
return true;
}
case base::DIR_HOME:
- *result = file_util::GetHomeDir();
+ *result = GetHomeDir();
return true;
}
return false;
diff --git a/chromium/base/base_switches.cc b/chromium/base/base_switches.cc
index bdd7b62d475..91e401ca497 100644
--- a/chromium/base/base_switches.cc
+++ b/chromium/base/base_switches.cc
@@ -15,6 +15,11 @@ const char kDebugOnStart[] = "debug-on-start";
// Disables the crash reporting.
const char kDisableBreakpad[] = "disable-breakpad";
+// Indicates that crash reporting should be enabled. On platforms where helper
+// processes cannot access to files needed to make this decision, this flag is
+// generated internally.
+const char kEnableCrashReporter[] = "enable-crash-reporter";
+
// Enable DCHECKs in release mode.
const char kEnableDCHECK[] = "enable-dcheck";
@@ -49,12 +54,27 @@ const char kWaitForDebugger[] = "wait-for-debugger";
// Sends a pretty-printed version of tracing info to the console.
const char kTraceToConsole[] = "trace-to-console";
+// Configure whether chrome://profiler will contain timing information. This
+// option is enabled by default. A value of "0" will disable profiler timing,
+// while all other values will enable it.
+const char kProfilerTiming[] = "profiler-timing";
+// Value of the --profiler-timing flag that will disable timing information for
+// chrome://profiler.
+const char kProfilerTimingDisabledValue[] = "0";
+
#if defined(OS_POSIX)
-// A flag, generated internally for renderer and other helper process command
-// lines on Linux and Mac. It tells the helper process to enable crash dumping
-// and reporting, because helpers cannot access the files needed to make this
-// decision.
-const char kEnableCrashReporter[] = "enable-crash-reporter";
+// Used for turning on Breakpad crash reporting in a debug environment where
+// crash reporting is typically compiled but disabled.
+const char kEnableCrashReporterForTesting[] =
+ "enable-crash-reporter-for-testing";
+#endif
+
+#if defined(OS_ANDROID)
+// Overrides low-end device detection, disabling low-end device optimizations.
+const char kDisableLowEndDeviceMode[] = "disable-low-end-device-mode";
+
+// Overrides low-end device detection, enabling low-end device optimizations.
+const char kEnableLowEndDeviceMode[] = "enable-low-end-device-mode";
#endif
} // namespace switches
diff --git a/chromium/base/base_switches.h b/chromium/base/base_switches.h
index 7686e76316e..b679e6d9ebf 100644
--- a/chromium/base/base_switches.h
+++ b/chromium/base/base_switches.h
@@ -13,17 +13,25 @@ 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[];
+extern const char kProfilerTimingDisabledValue[];
extern const char kTestChildProcess[];
+extern const char kTraceToConsole[];
extern const char kV[];
extern const char kVModule[];
extern const char kWaitForDebugger[];
-extern const char kTraceToConsole[];
#if defined(OS_POSIX)
-extern const char kEnableCrashReporter[];
+extern const char kEnableCrashReporterForTesting[];
+#endif
+
+#if defined(OS_ANDROID)
+extern const char kDisableLowEndDeviceMode[];
+extern const char kEnableLowEndDeviceMode[];
#endif
} // namespace switches
diff --git a/chromium/base/base_unittests.isolate b/chromium/base/base_unittests.isolate
index 0b140dd945b..166cf801eca 100644
--- a/chromium/base/base_unittests.isolate
+++ b/chromium/base/base_unittests.isolate
@@ -16,8 +16,9 @@
'command': [
'../testing/xvfb.py',
'<(PRODUCT_DIR)',
- '../tools/swarm_client/googletest/run_test_cases.py',
'<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
],
'isolate_dependency_tracked': [
'../testing/xvfb.py',
@@ -32,7 +33,7 @@
'<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
],
'isolate_dependency_untracked': [
- '../tools/swarm_client/',
+ '../tools/swarming_client/',
],
},
}],
@@ -40,8 +41,9 @@
'variables': {
'command': [
'../testing/test_env.py',
- '../tools/swarm_client/googletest/run_test_cases.py',
'<(PRODUCT_DIR)/base_unittests<(EXECUTABLE_SUFFIX)',
+ '--brave-new-test-launcher',
+ '--test-launcher-bot-mode',
],
},
}],
diff --git a/chromium/base/basictypes.h b/chromium/base/basictypes.h
index 1bed65c6a51..e77d7b10f24 100644
--- a/chromium/base/basictypes.h
+++ b/chromium/base/basictypes.h
@@ -9,6 +9,7 @@
#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
#ifndef COMPILER_MSVC
@@ -215,13 +216,21 @@ inline To implicit_cast(From const &f) {
// 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 {
};
-#undef COMPILE_ASSERT
#define COMPILE_ASSERT(expr, msg) \
- typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ALLOW_UNUSED
// Implementation details of COMPILE_ASSERT:
//
@@ -264,6 +273,7 @@ struct CompileAssert {
// 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
@@ -321,9 +331,7 @@ struct CompileAssert {
template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
- // Compile time assertion: sizeof(Dest) == sizeof(Source)
- // A compile error here means your Dest and Source have different sizes.
- typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
+ COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
Dest dest;
memcpy(&dest, &source, sizeof(dest));
diff --git a/chromium/base/callback.h b/chromium/base/callback.h
index ade3f8c1fc0..a962957437c 100644
--- a/chromium/base/callback.h
+++ b/chromium/base/callback.h
@@ -214,11 +214,16 @@
//
// PASSING PARAMETERS BY REFERENCE
//
-// void foo(int arg) { cout << arg << endl }
+// Const references are *copied* unless ConstRef is used. Example:
+//
+// void foo(const int& arg) { printf("%d %p\n", arg, &arg); }
// int n = 1;
+// base::Closure has_copy = base::Bind(&foo, n);
// base::Closure has_ref = base::Bind(&foo, base::ConstRef(n));
// n = 2;
-// has_ref.Run(); // Prints "2"
+// foo(n); // Prints "2 0xaaaaaaaaaaaa"
+// has_copy.Run(); // Prints "1 0xbbbbbbbbbbbb"
+// has_ref.Run(); // Prints "2 0xaaaaaaaaaaaa"
//
// Normally parameters are copied in the closure. DANGER: ConstRef stores a
// const reference instead, referencing the original parameter. This means
diff --git a/chromium/base/callback_internal.h b/chromium/base/callback_internal.h
index 5993824a5a2..e66623cc513 100644
--- a/chromium/base/callback_internal.h
+++ b/chromium/base/callback_internal.h
@@ -67,6 +67,20 @@ class BASE_EXPORT CallbackBase {
InvokeFuncStorage polymorphic_invoke_;
};
+// A helper template to determine if given type is non-const move-only-type,
+// i.e. if a value of the given type should be passed via .Pass() in a
+// destructive way.
+template <typename T> struct IsMoveOnlyType {
+ template <typename U>
+ static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
+
+ template <typename U>
+ static NoType Test(...);
+
+ static const bool value = sizeof(Test<T>(0)) == sizeof(YesType) &&
+ !is_const<T>::value;
+};
+
// This is a typetraits object that's used to take an argument type, and
// extract a suitable type for storing and forwarding arguments.
//
@@ -78,7 +92,7 @@ class BASE_EXPORT CallbackBase {
// parameters by const reference. In this case, we end up passing an actual
// array type in the initializer list which C++ does not allow. This will
// break passing of C-string literals.
-template <typename T>
+template <typename T, bool is_move_only = IsMoveOnlyType<T>::value>
struct CallbackParamTraits {
typedef const T& ForwardType;
typedef T StorageType;
@@ -90,7 +104,7 @@ struct CallbackParamTraits {
//
// The ForwardType should only be used for unbound arguments.
template <typename T>
-struct CallbackParamTraits<T&> {
+struct CallbackParamTraits<T&, false> {
typedef T& ForwardType;
typedef T StorageType;
};
@@ -101,14 +115,14 @@ struct CallbackParamTraits<T&> {
// T[n]" does not seem to match correctly, so we are stuck with this
// restriction.
template <typename T, size_t n>
-struct CallbackParamTraits<T[n]> {
+struct CallbackParamTraits<T[n], false> {
typedef const T* ForwardType;
typedef const T* StorageType;
};
// See comment for CallbackParamTraits<T[n]>.
template <typename T>
-struct CallbackParamTraits<T[]> {
+struct CallbackParamTraits<T[], false> {
typedef const T* ForwardType;
typedef const T* StorageType;
};
@@ -126,26 +140,10 @@ struct CallbackParamTraits<T[]> {
// correctness, because we are implementing a destructive move. A non-const
// reference cannot be used with temporaries which means the result of a
// function or a cast would not be usable with Callback<> or Bind().
-//
-// TODO(ajwong): We might be able to use SFINAE to search for the existence of
-// a Pass() function in the type and avoid the whitelist in CallbackParamTraits
-// and CallbackForward.
-template <typename T, typename D>
-struct CallbackParamTraits<scoped_ptr<T, D> > {
- typedef scoped_ptr<T, D> ForwardType;
- typedef scoped_ptr<T, D> StorageType;
-};
-
-template <typename T, typename R>
-struct CallbackParamTraits<scoped_ptr_malloc<T, R> > {
- typedef scoped_ptr_malloc<T, R> ForwardType;
- typedef scoped_ptr_malloc<T, R> StorageType;
-};
-
template <typename T>
-struct CallbackParamTraits<ScopedVector<T> > {
- typedef ScopedVector<T> ForwardType;
- typedef ScopedVector<T> StorageType;
+struct CallbackParamTraits<T, true> {
+ typedef T ForwardType;
+ typedef T StorageType;
};
// CallbackForward() is a very limited simulation of C++11's std::forward()
@@ -165,18 +163,14 @@ struct CallbackParamTraits<ScopedVector<T> > {
// parameter to another callback. This is to support Callbacks that return
// the movable-but-not-copyable types whitelisted above.
template <typename T>
-T& CallbackForward(T& t) { return t; }
-
-template <typename T, typename D>
-scoped_ptr<T, D> CallbackForward(scoped_ptr<T, D>& p) { return p.Pass(); }
-
-template <typename T, typename R>
-scoped_ptr_malloc<T, R> CallbackForward(scoped_ptr_malloc<T, R>& p) {
- return p.Pass();
+typename enable_if<!IsMoveOnlyType<T>::value, T>::type& CallbackForward(T& t) {
+ return t;
}
template <typename T>
-ScopedVector<T> CallbackForward(ScopedVector<T>& p) { return p.Pass(); }
+typename enable_if<IsMoveOnlyType<T>::value, T>::type CallbackForward(T& t) {
+ return t.Pass();
+}
} // namespace internal
} // namespace base
diff --git a/chromium/base/callback_list.h b/chromium/base/callback_list.h
new file mode 100644
index 00000000000..d12a1e95baa
--- /dev/null
+++ b/chromium/base/callback_list.h
@@ -0,0 +1,384 @@
+// This file was GENERATED by command:
+// pump.py callback_list.h.pump
+// DO NOT EDIT BY HAND!!!
+
+
+// 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_CALLBACK_LIST_H_
+#define BASE_CALLBACK_LIST_H_
+
+#include <list>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/callback_internal.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+
+// OVERVIEW:
+//
+// A container for a list of callbacks. Unlike a normal STL vector or list,
+// this container can be modified during iteration without invalidating the
+// iterator. It safely handles the case of a callback removing itself
+// or another callback from the list while callbacks are being run.
+//
+// TYPICAL USAGE:
+//
+// class MyWidget {
+// public:
+// ...
+//
+// typedef base::Callback<void(const Foo&)> OnFooCallback;
+//
+// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+// RegisterCallback(const OnFooCallback& cb) {
+// return callback_list_.Add(cb);
+// }
+//
+// private:
+// void NotifyFoo(const Foo& foo) {
+// callback_list_.Notify(foo);
+// }
+//
+// base::CallbackList<void(const Foo&)> callback_list_;
+//
+// DISALLOW_COPY_AND_ASSIGN(MyWidget);
+// };
+//
+//
+// class MyWidgetListener {
+// public:
+// MyWidgetListener::MyWidgetListener() {
+// foo_subscription_ = MyWidget::GetCurrent()->RegisterCallback(
+// base::Bind(&MyWidgetListener::OnFoo, this)));
+// }
+//
+// MyWidgetListener::~MyWidgetListener() {
+// // Subscription gets deleted automatically and will deregister
+// // the callback in the process.
+// }
+//
+// private:
+// void OnFoo(const Foo& foo) {
+// // Do something.
+// }
+//
+// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+// foo_subscription_;
+//
+// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
+// };
+
+namespace base {
+
+namespace internal {
+
+template <typename CallbackType>
+class CallbackListBase {
+ public:
+ class Subscription {
+ public:
+ Subscription(CallbackListBase<CallbackType>* list,
+ typename std::list<CallbackType>::iterator iter)
+ : list_(list),
+ iter_(iter) {
+ }
+
+ ~Subscription() {
+ if (list_->active_iterator_count_)
+ iter_->Reset();
+ else
+ list_->callbacks_.erase(iter_);
+ }
+
+ private:
+ CallbackListBase<CallbackType>* list_;
+ typename std::list<CallbackType>::iterator iter_;
+
+ DISALLOW_COPY_AND_ASSIGN(Subscription);
+ };
+
+ // Add a callback to the list. The callback will remain registered until the
+ // returned Subscription is destroyed, which must occur before the
+ // CallbackList is destroyed.
+ scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
+ DCHECK(!cb.is_null());
+ return scoped_ptr<Subscription>(
+ new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
+ }
+
+ protected:
+ // An iterator class that can be used to access the list of callbacks.
+ class Iterator {
+ public:
+ explicit Iterator(CallbackListBase<CallbackType>* list)
+ : list_(list),
+ list_iter_(list_->callbacks_.begin()) {
+ ++list_->active_iterator_count_;
+ }
+
+ Iterator(const Iterator& iter)
+ : list_(iter.list_),
+ list_iter_(iter.list_iter_) {
+ ++list_->active_iterator_count_;
+ }
+
+ ~Iterator() {
+ if (list_ && --list_->active_iterator_count_ == 0) {
+ list_->Compact();
+ }
+ }
+
+ CallbackType* GetNext() {
+ while ((list_iter_ != list_->callbacks_.end()) && list_iter_->is_null())
+ ++list_iter_;
+
+ CallbackType* cb = NULL;
+ if (list_iter_ != list_->callbacks_.end()) {
+ cb = &(*list_iter_);
+ ++list_iter_;
+ }
+ return cb;
+ }
+
+ private:
+ CallbackListBase<CallbackType>* list_;
+ typename std::list<CallbackType>::iterator list_iter_;
+ };
+
+ CallbackListBase() : active_iterator_count_(0) {}
+
+ ~CallbackListBase() {
+ DCHECK_EQ(0, active_iterator_count_);
+ DCHECK_EQ(0U, callbacks_.size());
+ }
+
+ // Returns an instance of a CallbackListBase::Iterator which can be used
+ // to run callbacks.
+ Iterator GetIterator() {
+ return Iterator(this);
+ }
+
+ // Compact the list: remove any entries which were NULLed out during
+ // iteration.
+ void Compact() {
+ typename std::list<CallbackType>::iterator it = callbacks_.begin();
+ while (it != callbacks_.end()) {
+ if ((*it).is_null())
+ it = callbacks_.erase(it);
+ else
+ ++it;
+ }
+ }
+
+ private:
+ std::list<CallbackType> callbacks_;
+ int active_iterator_count_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
+};
+
+} // namespace internal
+
+template <typename Sig> class CallbackList;
+
+template <>
+class CallbackList<void(void)>
+ : public internal::CallbackListBase<Callback<void(void)> > {
+ public:
+ typedef Callback<void(void)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify() {
+ internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run();
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1>
+class CallbackList<void(A1)>
+ : public internal::CallbackListBase<Callback<void(A1)> > {
+ public:
+ typedef Callback<void(A1)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2>
+class CallbackList<void(A1, A2)>
+ : public internal::CallbackListBase<Callback<void(A1, A2)> > {
+ public:
+ typedef Callback<void(A1, A2)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3>
+class CallbackList<void(A1, A2, A3)>
+ : public internal::CallbackListBase<Callback<void(A1, A2, A3)> > {
+ public:
+ typedef Callback<void(A1, A2, A3)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2, a3);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4>
+class CallbackList<void(A1, A2, A3, A4)>
+ : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4)> > {
+ public:
+ typedef Callback<void(A1, A2, A3, A4)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2, a3, a4);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5>
+class CallbackList<void(A1, A2, A3, A4, A5)>
+ : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5)> > {
+ public:
+ typedef Callback<void(A1, A2, A3, A4, A5)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2, a3, a4, a5);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6>
+class CallbackList<void(A1, A2, A3, A4, A5, A6)>
+ : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5,
+ A6)> > {
+ public:
+ typedef Callback<void(A1, A2, A3, A4, A5, A6)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5,
+ typename internal::CallbackParamTraits<A6>::ForwardType a6) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2, a3, a4, a5, a6);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+template <typename A1, typename A2, typename A3, typename A4, typename A5,
+ typename A6, typename A7>
+class CallbackList<void(A1, A2, A3, A4, A5, A6, A7)>
+ : public internal::CallbackListBase<Callback<void(A1, A2, A3, A4, A5, A6,
+ A7)> > {
+ public:
+ typedef Callback<void(A1, A2, A3, A4, A5, A6, A7)> CallbackType;
+
+ CallbackList() {}
+
+ void Notify(typename internal::CallbackParamTraits<A1>::ForwardType a1,
+ typename internal::CallbackParamTraits<A2>::ForwardType a2,
+ typename internal::CallbackParamTraits<A3>::ForwardType a3,
+ typename internal::CallbackParamTraits<A4>::ForwardType a4,
+ typename internal::CallbackParamTraits<A5>::ForwardType a5,
+ typename internal::CallbackParamTraits<A6>::ForwardType a6,
+ typename internal::CallbackParamTraits<A7>::ForwardType a7) {
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run(a1, a2, a3, a4, a5, a6, a7);
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
+};
+
+} // namespace base
+
+#endif // BASE_CALLBACK_LIST_H_
diff --git a/chromium/base/callback_registry.h b/chromium/base/callback_list.h.pump
index fcacbf549f2..ea2103ebeef 100644
--- a/chromium/base/callback_registry.h
+++ b/chromium/base/callback_list.h.pump
@@ -1,14 +1,25 @@
+$$ This is a pump file for generating file templates. Pump is a python
+$$ script that is part of the Google Test suite of utilities. Description
+$$ can be found here:
+$$
+$$ http://code.google.com/p/googletest/wiki/PumpManual
+$$
+
+$$ See comment for MAX_ARITY in base/bind.h.pump.
+$var MAX_ARITY = 7
+
// Copyright 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_CALLBACK_REGISTRY_H_
-#define BASE_CALLBACK_REGISTRY_H_
+#ifndef BASE_CALLBACK_LIST_H_
+#define BASE_CALLBACK_LIST_H_
#include <list>
#include "base/basictypes.h"
#include "base/callback.h"
+#include "base/callback_internal.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
@@ -28,17 +39,19 @@
//
// typedef base::Callback<void(const Foo&)> OnFooCallback;
//
-// scoped_ptr<base::CallbackRegistry<Foo>::Subscription> RegisterCallback(
-// const OnFooCallback& cb) {
-// return callback_registry_.Add(cb);
+// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+// RegisterCallback(const OnFooCallback& cb) {
+// return callback_list_.Add(cb);
// }
//
// private:
// void NotifyFoo(const Foo& foo) {
-// callback_registry_.Notify(foo);
+// callback_list_.Notify(foo);
// }
//
-// base::CallbackRegistry<Foo> callback_registry_;
+// base::CallbackList<void(const Foo&)> callback_list_;
+//
+// DISALLOW_COPY_AND_ASSIGN(MyWidget);
// };
//
//
@@ -59,7 +72,10 @@
// // Do something.
// }
//
-// scoped_ptr<base::CallbackRegistry<Foo>::Subscription> foo_subscription_;
+// scoped_ptr<base::CallbackList<void(const Foo&)>::Subscription>
+// foo_subscription_;
+//
+// DISALLOW_COPY_AND_ASSIGN(MyWidgetListener);
// };
namespace base {
@@ -67,24 +83,25 @@ namespace base {
namespace internal {
template <typename CallbackType>
-class CallbackRegistryBase {
+class CallbackListBase {
public:
class Subscription {
public:
- Subscription(CallbackRegistryBase<CallbackType>* list,
+ Subscription(CallbackListBase<CallbackType>* list,
typename std::list<CallbackType>::iterator iter)
: list_(list),
- iter_(iter) {}
+ iter_(iter) {
+ }
~Subscription() {
if (list_->active_iterator_count_)
- (*iter_).Reset();
+ iter_->Reset();
else
list_->callbacks_.erase(iter_);
}
private:
- CallbackRegistryBase<CallbackType>* list_;
+ CallbackListBase<CallbackType>* list_;
typename std::list<CallbackType>::iterator iter_;
DISALLOW_COPY_AND_ASSIGN(Subscription);
@@ -92,8 +109,8 @@ class CallbackRegistryBase {
// Add a callback to the list. The callback will remain registered until the
// returned Subscription is destroyed, which must occur before the
- // CallbackRegistry is destroyed.
- scoped_ptr<Subscription> Add(const CallbackType& cb) {
+ // CallbackList is destroyed.
+ scoped_ptr<Subscription> Add(const CallbackType& cb) WARN_UNUSED_RESULT {
DCHECK(!cb.is_null());
return scoped_ptr<Subscription>(
new Subscription(this, callbacks_.insert(callbacks_.end(), cb)));
@@ -103,7 +120,7 @@ class CallbackRegistryBase {
// An iterator class that can be used to access the list of callbacks.
class Iterator {
public:
- explicit Iterator(CallbackRegistryBase<CallbackType>* list)
+ explicit Iterator(CallbackListBase<CallbackType>* list)
: list_(list),
list_iter_(list_->callbacks_.begin()) {
++list_->active_iterator_count_;
@@ -134,19 +151,18 @@ class CallbackRegistryBase {
}
private:
- CallbackRegistryBase<CallbackType>* list_;
+ CallbackListBase<CallbackType>* list_;
typename std::list<CallbackType>::iterator list_iter_;
};
- CallbackRegistryBase()
- : active_iterator_count_(0) {}
+ CallbackListBase() : active_iterator_count_(0) {}
- ~CallbackRegistryBase() {
+ ~CallbackListBase() {
DCHECK_EQ(0, active_iterator_count_);
DCHECK_EQ(0U, callbacks_.size());
}
- // Returns an instance of a CallbackRegistryBase::Iterator which can be used
+ // Returns an instance of a CallbackListBase::Iterator which can be used
// to run callbacks.
Iterator GetIterator() {
return Iterator(this);
@@ -168,49 +184,64 @@ class CallbackRegistryBase {
std::list<CallbackType> callbacks_;
int active_iterator_count_;
- DISALLOW_COPY_AND_ASSIGN(CallbackRegistryBase);
+ DISALLOW_COPY_AND_ASSIGN(CallbackListBase);
};
} // namespace internal
-template <typename Details>
-class CallbackRegistry
- : public internal::CallbackRegistryBase<Callback<void(const Details&)> > {
- public:
- CallbackRegistry() {}
-
- // Execute all active callbacks with |details| parameter.
- void Notify(const Details& details) {
- typename internal::CallbackRegistryBase<
- Callback<void(const Details&)> >::Iterator it = this->GetIterator();
- Callback<void(const Details&)>* cb;
- while((cb = it.GetNext()) != NULL) {
- cb->Run(details);
- }
- }
+template <typename Sig> class CallbackList;
-private:
- DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
-};
-template <> class CallbackRegistry<void>
- : public internal::CallbackRegistryBase<Closure> {
+$range ARITY 0..MAX_ARITY
+$for ARITY [[
+$range ARG 1..ARITY
+
+$if ARITY == 0 [[
+template <>
+class CallbackList<void(void)>
+ : public internal::CallbackListBase<Callback<void(void)> > {
+]] $else [[
+template <$for ARG , [[typename A$(ARG)]]>
+class CallbackList<void($for ARG , [[A$(ARG)]])>
+ : public internal::CallbackListBase<Callback<void($for ARG , [[A$(ARG)]])> > {
+]]
+
public:
- CallbackRegistry() {}
-
- // Execute all active callbacks.
- void Notify() {
- Iterator it = this->GetIterator();
- Closure* cb;
- while((cb = it.GetNext()) != NULL) {
- cb->Run();
+$if ARITY == 0 [[
+
+ typedef Callback<void(void)> CallbackType;
+]] $else [[
+
+ typedef Callback<void($for ARG , [[A$(ARG)]])> CallbackType;
+]]
+
+
+ CallbackList() {}
+
+ void Notify($for ARG ,
+ [[typename internal::CallbackParamTraits<A$(ARG)>::ForwardType a$(ARG)]]) {
+$if ARITY == 0 [[
+
+ internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+]] $else [[
+
+ typename internal::CallbackListBase<CallbackType>::Iterator it =
+ this->GetIterator();
+]]
+
+ CallbackType* cb;
+ while ((cb = it.GetNext()) != NULL) {
+ cb->Run($for ARG , [[a$(ARG)]]);
}
}
private:
- DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
+ DISALLOW_COPY_AND_ASSIGN(CallbackList);
};
+
+]] $$ for ARITY
} // namespace base
-#endif // BASE_CALLBACK_REGISTRY_H_
+#endif // BASE_CALLBACK_LIST_H_
diff --git a/chromium/base/callback_list_unittest.cc b/chromium/base/callback_list_unittest.cc
new file mode 100644
index 00000000000..9adbabb0931
--- /dev/null
+++ b/chromium/base/callback_list_unittest.cc
@@ -0,0 +1,291 @@
+// 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/callback_list.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+class Listener {
+ public:
+ Listener() : total_(0), scaler_(1) {}
+ explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
+ void IncrementTotal() { total_++; }
+ void IncrementByMultipleOfScaler(int x) { total_ += x * scaler_; }
+
+ int total() const { return total_; }
+
+ private:
+ int total_;
+ int scaler_;
+ DISALLOW_COPY_AND_ASSIGN(Listener);
+};
+
+class Remover {
+ public:
+ Remover() : total_(0) {}
+ void IncrementTotalAndRemove() {
+ total_++;
+ removal_subscription_.reset();
+ }
+ void SetSubscriptionToRemove(
+ scoped_ptr<CallbackList<void(void)>::Subscription> sub) {
+ removal_subscription_ = sub.Pass();
+ }
+
+ int total() const { return total_; }
+
+ private:
+ int total_;
+ scoped_ptr<CallbackList<void(void)>::Subscription> removal_subscription_;
+ DISALLOW_COPY_AND_ASSIGN(Remover);
+};
+
+class Adder {
+ public:
+ explicit Adder(CallbackList<void(void)>* cb_reg)
+ : added_(false),
+ total_(0),
+ cb_reg_(cb_reg) {
+ }
+ void AddCallback() {
+ if (!added_) {
+ added_ = true;
+ subscription_ =
+ cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this)));
+ }
+ }
+ void IncrementTotal() { total_++; }
+
+ bool added() const { return added_; }
+
+ int total() const { return total_; }
+
+ private:
+ bool added_;
+ int total_;
+ CallbackList<void(void)>* cb_reg_;
+ scoped_ptr<CallbackList<void(void)>::Subscription> subscription_;
+ DISALLOW_COPY_AND_ASSIGN(Adder);
+};
+
+class Summer {
+ public:
+ Summer() : value_(0) {}
+
+ void AddOneParam(int a) { value_ = a; }
+ void AddTwoParam(int a, int b) { value_ = a + b; }
+ void AddThreeParam(int a, int b, int c) { value_ = a + b + c; }
+ void AddFourParam(int a, int b, int c, int d) { value_ = a + b + c + d; }
+ void AddFiveParam(int a, int b, int c, int d, int e) {
+ value_ = a + b + c + d + e;
+ }
+ void AddSixParam(int a, int b, int c, int d, int e , int f) {
+ value_ = a + b + c + d + e + f;
+ }
+
+ int value() const { return value_; }
+
+ private:
+ int value_;
+ DISALLOW_COPY_AND_ASSIGN(Summer);
+};
+
+// Sanity check that we can instantiate a CallbackList for each arity.
+TEST(CallbackListTest, ArityTest) {
+ Summer s;
+
+ CallbackList<void(int)> c1;
+ scoped_ptr<CallbackList<void(int)>::Subscription> subscription1 =
+ c1.Add(Bind(&Summer::AddOneParam, Unretained(&s)));
+
+ c1.Notify(1);
+ EXPECT_EQ(1, s.value());
+
+ CallbackList<void(int, int)> c2;
+ scoped_ptr<CallbackList<void(int, int)>::Subscription> subscription2 =
+ c2.Add(Bind(&Summer::AddTwoParam, Unretained(&s)));
+
+ c2.Notify(1, 2);
+ EXPECT_EQ(3, s.value());
+
+ CallbackList<void(int, int, int)> c3;
+ scoped_ptr<CallbackList<void(int, int, int)>::Subscription>
+ subscription3 = c3.Add(Bind(&Summer::AddThreeParam, Unretained(&s)));
+
+ c3.Notify(1, 2, 3);
+ EXPECT_EQ(6, s.value());
+
+ CallbackList<void(int, int, int, int)> c4;
+ scoped_ptr<CallbackList<void(int, int, int, int)>::Subscription>
+ subscription4 = c4.Add(Bind(&Summer::AddFourParam, Unretained(&s)));
+
+ c4.Notify(1, 2, 3, 4);
+ EXPECT_EQ(10, s.value());
+
+ CallbackList<void(int, int, int, int, int)> c5;
+ scoped_ptr<CallbackList<void(int, int, int, int, int)>::Subscription>
+ subscription5 = c5.Add(Bind(&Summer::AddFiveParam, Unretained(&s)));
+
+ c5.Notify(1, 2, 3, 4, 5);
+ EXPECT_EQ(15, s.value());
+
+ CallbackList<void(int, int, int, int, int, int)> c6;
+ scoped_ptr<CallbackList<void(int, int, int, int, int, int)>::Subscription>
+ subscription6 = c6.Add(Bind(&Summer::AddSixParam, Unretained(&s)));
+
+ c6.Notify(1, 2, 3, 4, 5, 6);
+ EXPECT_EQ(21, s.value());
+}
+
+// Sanity check that closures added to the list will be run, and those removed
+// from the list will not be run.
+TEST(CallbackListTest, BasicTest) {
+ CallbackList<void(void)> cb_reg;
+ Listener a, b, c;
+
+ scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
+ scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
+
+ EXPECT_TRUE(a_subscription.get());
+ EXPECT_TRUE(b_subscription.get());
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(1, a.total());
+ EXPECT_EQ(1, b.total());
+
+ b_subscription.reset();
+
+ scoped_ptr<CallbackList<void(void)>::Subscription> c_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c)));
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(2, a.total());
+ EXPECT_EQ(1, b.total());
+ EXPECT_EQ(1, c.total());
+
+ a_subscription.reset();
+ b_subscription.reset();
+ c_subscription.reset();
+}
+
+// Sanity check that callbacks with details added to the list will be run, with
+// the correct details, and those removed from the list will not be run.
+TEST(CallbackListTest, BasicTestWithParams) {
+ CallbackList<void(int)> cb_reg;
+ Listener a(1), b(-1), c(1);
+
+ scoped_ptr<CallbackList<void(int)>::Subscription> a_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
+ scoped_ptr<CallbackList<void(int)>::Subscription> b_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
+
+ EXPECT_TRUE(a_subscription.get());
+ EXPECT_TRUE(b_subscription.get());
+
+ cb_reg.Notify(10);
+
+ EXPECT_EQ(10, a.total());
+ EXPECT_EQ(-10, b.total());
+
+ b_subscription.reset();
+
+ scoped_ptr<CallbackList<void(int)>::Subscription> c_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
+
+ cb_reg.Notify(10);
+
+ EXPECT_EQ(20, a.total());
+ EXPECT_EQ(-10, b.total());
+ EXPECT_EQ(10, c.total());
+
+ a_subscription.reset();
+ b_subscription.reset();
+ c_subscription.reset();
+}
+
+// Test the a callback can remove itself or a different callback from the list
+// during iteration without invalidating the iterator.
+TEST(CallbackListTest, RemoveCallbacksDuringIteration) {
+ CallbackList<void(void)> cb_reg;
+ Listener a, b;
+ Remover remover_1, remover_2;
+
+ scoped_ptr<CallbackList<void(void)>::Subscription> remover_1_sub =
+ cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
+ Unretained(&remover_1)));
+ scoped_ptr<CallbackList<void(void)>::Subscription> remover_2_sub =
+ cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
+ Unretained(&remover_2)));
+ scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
+ scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
+
+ // |remover_1| will remove itself.
+ remover_1.SetSubscriptionToRemove(remover_1_sub.Pass());
+ // |remover_2| will remove a.
+ remover_2.SetSubscriptionToRemove(a_subscription.Pass());
+
+ cb_reg.Notify();
+
+ // |remover_1| runs once (and removes itself), |remover_2| runs once (and
+ // removes a), |a| never runs, and |b| runs once.
+ EXPECT_EQ(1, remover_1.total());
+ EXPECT_EQ(1, remover_2.total());
+ EXPECT_EQ(0, a.total());
+ EXPECT_EQ(1, b.total());
+
+ cb_reg.Notify();
+
+ // Only |remover_2| and |b| run this time.
+ EXPECT_EQ(1, remover_1.total());
+ EXPECT_EQ(2, remover_2.total());
+ EXPECT_EQ(0, a.total());
+ EXPECT_EQ(2, b.total());
+}
+
+// Test that a callback can add another callback to the list durning iteration
+// without invalidating the iterator. The newly added callback should be run on
+// the current iteration as will all other callbacks in the list.
+TEST(CallbackListTest, AddCallbacksDuringIteration) {
+ CallbackList<void(void)> cb_reg;
+ Adder a(&cb_reg);
+ Listener b;
+ scoped_ptr<CallbackList<void(void)>::Subscription> a_subscription =
+ cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a)));
+ scoped_ptr<CallbackList<void(void)>::Subscription> b_subscription =
+ cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(1, a.total());
+ EXPECT_EQ(1, b.total());
+ EXPECT_TRUE(a.added());
+
+ cb_reg.Notify();
+
+ EXPECT_EQ(2, a.total());
+ EXPECT_EQ(2, b.total());
+}
+
+// Sanity check: notifying an empty list is a no-op.
+TEST(CallbackListTest, EmptyList) {
+ CallbackList<void(void)> cb_reg;
+
+ cb_reg.Notify();
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/callback_list_unittest.nc b/chromium/base/callback_list_unittest.nc
new file mode 100644
index 00000000000..2d464cf1f1b
--- /dev/null
+++ b/chromium/base/callback_list_unittest.nc
@@ -0,0 +1,51 @@
+// 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/callback_list.h"
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace base {
+
+class Foo {
+ public:
+ Foo() {}
+ ~Foo() {}
+};
+
+class FooListener {
+ public:
+ FooListener() {}
+
+ void GotAScopedFoo(scoped_ptr<Foo> f) { foo_ = f.Pass(); }
+
+ scoped_ptr<Foo> foo_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FooListener);
+};
+
+
+#if defined(NCTEST_MOVE_ONLY_TYPE_PARAMETER) // [r"calling a private constructor of class"]
+
+// Callbacks run with a move-only typed parameter.
+//
+// CallbackList does not support move-only typed parameters. Notify() is
+// designed to take zero or more parameters, and run each registered callback
+// with them. With move-only types, the parameter will be set to NULL after the
+// first callback has been run.
+void WontCompile() {
+ FooListener f;
+ CallbackList<void(scoped_ptr<Foo>)> c1;
+ scoped_ptr<CallbackList<void(scoped_ptr<Foo>)>::Subscription> sub =
+ c1.Add(Bind(&FooListener::GotAScopedFoo, Unretained(&f)));
+ c1.Notify(scoped_ptr<Foo>(new Foo()));
+}
+
+#endif
+
+} // namespace base
diff --git a/chromium/base/callback_registry_unittest.cc b/chromium/base/callback_registry_unittest.cc
deleted file mode 100644
index 3459c073f16..00000000000
--- a/chromium/base/callback_registry_unittest.cc
+++ /dev/null
@@ -1,216 +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/callback_registry.h"
-
-#include "base/basictypes.h"
-#include "base/bind.h"
-#include "base/bind_helpers.h"
-#include "base/memory/scoped_ptr.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace base {
-namespace {
-
-class Listener {
- public:
- Listener() : total_(0), scaler_(1) {}
- explicit Listener(int scaler) : total_(0), scaler_(scaler) {}
- void IncrementTotal() { total_++; }
- void IncrementByMultipleOfScaler(const int& x) { total_ += x * scaler_; }
-
- int total_;
-
- private:
- int scaler_;
- DISALLOW_COPY_AND_ASSIGN(Listener);
-};
-
-class Remover {
- public:
- Remover() : total_(0) {}
- void IncrementTotalAndRemove() {
- total_++;
- removal_subscription_.reset();
- }
- void SetSubscriptionToRemove(
- scoped_ptr<CallbackRegistry<void>::Subscription> sub) {
- removal_subscription_ = sub.Pass();
- }
-
- int total_;
-
- private:
- scoped_ptr<CallbackRegistry<void>::Subscription> removal_subscription_;
- DISALLOW_COPY_AND_ASSIGN(Remover);
-};
-
-class Adder {
- public:
- explicit Adder(CallbackRegistry<void>* cb_reg)
- : added_(false),
- total_(0),
- cb_reg_(cb_reg) {}
- void AddCallback() {
- if (!added_) {
- added_ = true;
- subscription_ =
- cb_reg_->Add(Bind(&Adder::IncrementTotal, Unretained(this)));
- }
- }
- void IncrementTotal() { total_++; }
-
- bool added_;
- int total_;
-
- private:
- CallbackRegistry<void>* cb_reg_;
- scoped_ptr<CallbackRegistry<void>::Subscription> subscription_;
- DISALLOW_COPY_AND_ASSIGN(Adder);
-};
-
-// Sanity check that closures added to the list will be run, and those removed
-// from the list will not be run.
-TEST(CallbackRegistryTest, BasicTest) {
- CallbackRegistry<void> cb_reg;
- Listener a, b, c;
-
- scoped_ptr<CallbackRegistry<void>::Subscription> a_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
- scoped_ptr<CallbackRegistry<void>::Subscription> b_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
-
- EXPECT_TRUE(a_subscription.get());
- EXPECT_TRUE(b_subscription.get());
-
- cb_reg.Notify();
-
- EXPECT_EQ(1, a.total_);
- EXPECT_EQ(1, b.total_);
-
- b_subscription.reset();
-
- scoped_ptr<CallbackRegistry<void>::Subscription> c_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&c)));
-
- cb_reg.Notify();
-
- EXPECT_EQ(2, a.total_);
- EXPECT_EQ(1, b.total_);
- EXPECT_EQ(1, c.total_);
-
- a_subscription.reset();
- b_subscription.reset();
- c_subscription.reset();
-}
-
-// Sanity check that callbacks with details added to the list will be run, with
-// the correct details, and those removed from the list will not be run.
-TEST(CallbackRegistryTest, BasicTestWithParams) {
- CallbackRegistry<int> cb_reg;
- Listener a(1), b(-1), c(1);
-
- scoped_ptr<CallbackRegistry<int>::Subscription> a_subscription =
- cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&a)));
- scoped_ptr<CallbackRegistry<int>::Subscription> b_subscription =
- cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&b)));
-
- EXPECT_TRUE(a_subscription.get());
- EXPECT_TRUE(b_subscription.get());
-
- cb_reg.Notify(10);
-
- EXPECT_EQ(10, a.total_);
- EXPECT_EQ(-10, b.total_);
-
- b_subscription.reset();
-
- scoped_ptr<CallbackRegistry<int>::Subscription> c_subscription =
- cb_reg.Add(Bind(&Listener::IncrementByMultipleOfScaler, Unretained(&c)));
-
- cb_reg.Notify(10);
-
- EXPECT_EQ(20, a.total_);
- EXPECT_EQ(-10, b.total_);
- EXPECT_EQ(10, c.total_);
-
- a_subscription.reset();
- b_subscription.reset();
- c_subscription.reset();
-}
-
-// Test the a callback can remove itself or a different callback from the list
-// during iteration without invalidating the iterator.
-TEST(CallbackRegistryTest, RemoveCallbacksDuringIteration) {
- CallbackRegistry<void> cb_reg;
- Listener a, b;
- Remover remover_1, remover_2;
-
- scoped_ptr<CallbackRegistry<void>::Subscription> remover_1_subscription =
- cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_1)));
- scoped_ptr<CallbackRegistry<void>::Subscription> remover_2_subscription =
- cb_reg.Add(Bind(&Remover::IncrementTotalAndRemove,
- Unretained(&remover_2)));
- scoped_ptr<CallbackRegistry<void>::Subscription> a_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&a)));
- scoped_ptr<CallbackRegistry<void>::Subscription> b_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
-
- // |remover_1| will remove itself.
- remover_1.SetSubscriptionToRemove(remover_1_subscription.Pass());
- // |remover_2| will remove a.
- remover_2.SetSubscriptionToRemove(a_subscription.Pass());
-
- cb_reg.Notify();
-
- // |remover_1| runs once (and removes itself), |remover_2| runs once (and
- // removes a), |a| never runs, and |b| runs once.
- EXPECT_EQ(1, remover_1.total_);
- EXPECT_EQ(1, remover_2.total_);
- EXPECT_EQ(0, a.total_);
- EXPECT_EQ(1, b.total_);
-
- cb_reg.Notify();
-
- // Only |remover_2| and |b| run this time.
- EXPECT_EQ(1, remover_1.total_);
- EXPECT_EQ(2, remover_2.total_);
- EXPECT_EQ(0, a.total_);
- EXPECT_EQ(2, b.total_);
-}
-
-// Test that a callback can add another callback to the list durning iteration
-// without invalidating the iterator. The newly added callback should be run on
-// the current iteration as will all other callbacks in the list.
-TEST(CallbackRegistryTest, AddCallbacksDuringIteration) {
- CallbackRegistry<void> cb_reg;
- Adder a(&cb_reg);
- Listener b;
- scoped_ptr<CallbackRegistry<void>::Subscription> a_subscription =
- cb_reg.Add(Bind(&Adder::AddCallback, Unretained(&a)));
- scoped_ptr<CallbackRegistry<void>::Subscription> b_subscription =
- cb_reg.Add(Bind(&Listener::IncrementTotal, Unretained(&b)));
-
- cb_reg.Notify();
-
- EXPECT_EQ(1, a.total_);
- EXPECT_EQ(1, b.total_);
- EXPECT_TRUE(a.added_);
-
- cb_reg.Notify();
-
- EXPECT_EQ(2, a.total_);
- EXPECT_EQ(2, b.total_);
-}
-
-// Sanity check: notifying an empty list is a no-op.
-TEST(CallbackRegistryTest, EmptyList) {
- CallbackRegistry<void> cb_reg;
-
- cb_reg.Notify();
-}
-
-} // namespace
-} // namespace base
diff --git a/chromium/base/cancelable_callback.h b/chromium/base/cancelable_callback.h
index 8ef01996a93..1f534c3bef0 100644
--- a/chromium/base/cancelable_callback.h
+++ b/chromium/base/cancelable_callback.h
@@ -9,8 +9,8 @@
//
// NOTE:
//
-// Calling CancellableCallback::Cancel() brings the object back to its natural,
-// default-constructed state, i.e., CancellableCallback::callback() will return
+// Calling CancelableCallback::Cancel() brings the object back to its natural,
+// default-constructed state, i.e., CancelableCallback::callback() will return
// a null callback.
//
// THREAD-SAFETY:
diff --git a/chromium/base/chromeos/chromeos_version.cc b/chromium/base/chromeos/chromeos_version.cc
deleted file mode 100644
index 4a70cd5312a..00000000000
--- a/chromium/base/chromeos/chromeos_version.cc
+++ /dev/null
@@ -1,24 +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/chromeos/chromeos_version.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "base/logging.h"
-
-namespace base {
-namespace chromeos {
-
-bool IsRunningOnChromeOS() {
- // Check if the user name is chronos. Note that we don't go with
- // getuid() + getpwuid_r() as it may end up reading /etc/passwd, which
- // can be expensive.
- const char* user = getenv("USER");
- return user && strcmp(user, "chronos") == 0;
-}
-
-} // namespace chromeos
-} // namespace base
diff --git a/chromium/base/chromeos/chromeos_version.h b/chromium/base/chromeos/chromeos_version.h
deleted file mode 100644
index 25acd43a9f0..00000000000
--- a/chromium/base/chromeos/chromeos_version.h
+++ /dev/null
@@ -1,20 +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_CHROMEOS_CHROMEOS_VERSION_H_
-#define BASE_CHROMEOS_CHROMEOS_VERSION_H_
-
-#include "base/base_export.h"
-
-namespace base {
-namespace chromeos {
-
-// Returns true if the browser is running on Chrome OS.
-// Useful for implementing stubs for Linux desktop.
-BASE_EXPORT bool IsRunningOnChromeOS();
-
-} // namespace chromeos
-} // namespace base
-
-#endif // BASE_CHROMEOS_CHROMEOS_VERSION_H_
diff --git a/chromium/base/command_line.cc b/chromium/base/command_line.cc
index 36ac88f12c1..e00eee6bd89 100644
--- a/chromium/base/command_line.cc
+++ b/chromium/base/command_line.cc
@@ -27,17 +27,22 @@ CommandLine* CommandLine::current_process_commandline_ = NULL;
namespace {
const CommandLine::CharType kSwitchTerminator[] = FILE_PATH_LITERAL("--");
const CommandLine::CharType kSwitchValueSeparator[] = FILE_PATH_LITERAL("=");
+
// Since we use a lazy match, make sure that longer versions (like "--") are
// listed before shorter versions (like "-") of similar prefixes.
#if defined(OS_WIN)
+// By putting slash last, we can control whether it is treaded as a switch
+// value by changing the value of switch_prefix_count to be one less than
+// the array size.
const CommandLine::CharType* const kSwitchPrefixes[] = {L"--", L"-", L"/"};
#elif defined(OS_POSIX)
// Unixes don't use slash as a switch.
const CommandLine::CharType* const kSwitchPrefixes[] = {"--", "-"};
#endif
+size_t switch_prefix_count = arraysize(kSwitchPrefixes);
size_t GetSwitchPrefixLength(const CommandLine::StringType& string) {
- for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
+ for (size_t i = 0; i < switch_prefix_count; ++i) {
CommandLine::StringType prefix(kSwitchPrefixes[i]);
if (string.compare(0, prefix.length(), prefix) == 0)
return prefix.length();
@@ -169,6 +174,15 @@ CommandLine::CommandLine(const StringVector& argv)
CommandLine::~CommandLine() {
}
+#if defined(OS_WIN)
+// static
+void CommandLine::set_slash_is_not_a_switch() {
+ // The last switch prefix should be slash, so adjust the size to skip it.
+ DCHECK(wcscmp(kSwitchPrefixes[arraysize(kSwitchPrefixes) - 1], L"/") == 0);
+ switch_prefix_count = arraysize(kSwitchPrefixes) - 1;
+}
+#endif
+
// static
bool CommandLine::Init(int argc, const char* const* argv) {
if (current_process_commandline_) {
@@ -307,8 +321,8 @@ FilePath CommandLine::GetSwitchValuePath(
CommandLine::StringType CommandLine::GetSwitchValueNative(
const std::string& switch_string) const {
- SwitchMap::const_iterator result = switches_.end();
- result = switches_.find(LowerASCIIOnWindows(switch_string));
+ SwitchMap::const_iterator result =
+ switches_.find(LowerASCIIOnWindows(switch_string));
return result == switches_.end() ? StringType() : result->second;
}
diff --git a/chromium/base/command_line.h b/chromium/base/command_line.h
index ed46c4f0d13..81bd4b73761 100644
--- a/chromium/base/command_line.h
+++ b/chromium/base/command_line.h
@@ -53,6 +53,17 @@ class BASE_EXPORT CommandLine {
~CommandLine();
+#if defined(OS_WIN)
+ // By default this class will treat command-line arguments beginning with
+ // slashes as switches on Windows, but not other platforms.
+ //
+ // If this behavior is inappropriate for your application, you can call this
+ // function BEFORE initializing the current process' global command line
+ // object and the behavior will be the same as Posix systems (only hyphens
+ // begin switches, everything else will be an arg).
+ static void set_slash_is_not_a_switch();
+#endif
+
// Initialize the current process CommandLine singleton. On Windows, ignores
// its arguments (we instead parse GetCommandLineW() directly) because we
// don't trust the CRT's parsing of the command line, but it still must be
diff --git a/chromium/base/compiler_specific.h b/chromium/base/compiler_specific.h
index 07b680b7844..dc4b2334987 100644
--- a/chromium/base/compiler_specific.h
+++ b/chromium/base/compiler_specific.h
@@ -68,6 +68,28 @@
#endif // COMPILER_MSVC
+// The C++ standard requires that static const members have an out-of-class
+// definition (in a single compilation unit), but MSVC chokes on this (when
+// language extensions, which are required, are enabled). (You're only likely to
+// notice the need for a definition if you take the address of the member or,
+// more commonly, pass it to a function that takes it as a reference argument --
+// probably an STL function.) This macro makes MSVC do the right thing. See
+// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more
+// information. Use like:
+//
+// In .h file:
+// struct Foo {
+// static const int kBar = 5;
+// };
+//
+// In .cc file:
+// STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar;
+#if defined(COMPILER_MSVC)
+#define STATIC_CONST_MEMBER_DEFINITION __declspec(selectany)
+#else
+#define STATIC_CONST_MEMBER_DEFINITION
+#endif
+
// Annotate a variable indicating it's ok if the variable is not used.
// (Typically used to silence a compiler warning when the assignment
// is important for some other reason.)
@@ -119,6 +141,10 @@
#define OVERRIDE override
#elif defined(__clang__)
#define OVERRIDE override
+#elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700
+// GCC 4.7 supports explicit virtual overrides when C++11 support is enabled.
+#define OVERRIDE override
#else
#define OVERRIDE
#endif
@@ -133,6 +159,10 @@
#define FINAL sealed
#elif defined(__clang__)
#define FINAL final
+#elif defined(COMPILER_GCC) && __cplusplus >= 201103 && \
+ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) >= 40700
+// GCC 4.7 supports explicit virtual overrides when C++11 support is enabled.
+#define FINAL final
#else
#define FINAL
#endif
diff --git a/chromium/base/containers/hash_tables.h b/chromium/base/containers/hash_tables.h
index 2a2b52f9da6..365b586ba93 100644
--- a/chromium/base/containers/hash_tables.h
+++ b/chromium/base/containers/hash_tables.h
@@ -103,7 +103,7 @@ DEFINE_TRIVIAL_HASH(unsigned long long);
}
DEFINE_STRING_HASH(std::string);
-DEFINE_STRING_HASH(string16);
+DEFINE_STRING_HASH(base::string16);
#undef DEFINE_STRING_HASH
diff --git a/chromium/base/containers/mru_cache.h b/chromium/base/containers/mru_cache.h
index e59e9098391..15ea2fccdc4 100644
--- a/chromium/base/containers/mru_cache.h
+++ b/chromium/base/containers/mru_cache.h
@@ -123,8 +123,6 @@ class MRUCacheBase {
// Retrieves the payload associated with a given key and returns it via
// result without affecting the ordering (unlike Get).
- //
- // TODO(brettw) We may want a const version of this function in the future.
iterator Peek(const KeyType& key) {
typename KeyIndex::const_iterator index_iter = index_.find(key);
if (index_iter == index_.end())
@@ -132,6 +130,13 @@ class MRUCacheBase {
return index_iter->second;
}
+ const_iterator Peek(const KeyType& key) const {
+ typename KeyIndex::const_iterator index_iter = index_.find(key);
+ if (index_iter == index_.end())
+ return end();
+ return index_iter->second;
+ }
+
// Erases the item referenced by the given iterator. An iterator to the item
// following it will be returned. The iterator must be valid.
iterator Erase(iterator pos) {
diff --git a/chromium/base/cpu.cc b/chromium/base/cpu.cc
index 1761529e046..66207a1e0b8 100644
--- a/chromium/base/cpu.cc
+++ b/chromium/base/cpu.cc
@@ -8,11 +8,13 @@
#include <algorithm>
+#include "base/basictypes.h"
#include "build/build_config.h"
#if defined(ARCH_CPU_X86_FAMILY)
#if defined(_MSC_VER)
#include <intrin.h>
+#include <immintrin.h> // For _xgetbv()
#endif
#endif
@@ -33,11 +35,16 @@ CPU::CPU()
has_ssse3_(false),
has_sse41_(false),
has_sse42_(false),
+ has_avx_(false),
+ has_avx_hardware_(false),
+ has_aesni_(false),
has_non_stop_time_stamp_counter_(false),
cpu_vendor_("unknown") {
Initialize();
}
+namespace {
+
#if defined(ARCH_CPU_X86_FAMILY)
#ifndef _MSC_VER
@@ -53,16 +60,6 @@ void __cpuid(int cpu_info[4], int info_type) {
);
}
-void __cpuidex(int cpu_info[4], int info_type, int info_index) {
- __asm__ volatile (
- "mov %%ebx, %%edi\n"
- "cpuid\n"
- "xchg %%edi, %%ebx\n"
- : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
- : "a"(info_type), "c"(info_index)
- );
-}
-
#else
void __cpuid(int cpu_info[4], int info_type) {
@@ -73,18 +70,22 @@ void __cpuid(int cpu_info[4], int info_type) {
);
}
-void __cpuidex(int cpu_info[4], int info_type, int info_index) {
- __asm__ volatile (
- "cpuid \n\t"
- : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
- : "a"(info_type), "c"(info_index)
- );
+#endif
+
+// _xgetbv returns the value of an Intel Extended Control Register (XCR).
+// Currently only XCR0 is defined by Intel so |xcr| should always be zero.
+uint64 _xgetbv(uint32 xcr) {
+ uint32 eax, edx;
+
+ __asm__ volatile ("xgetbv" : "=a" (eax), "=d" (edx) : "c" (xcr));
+ return (static_cast<uint64>(edx) << 32) | eax;
}
-#endif
-#endif // _MSC_VER
+#endif // !_MSC_VER
#endif // ARCH_CPU_X86_FAMILY
+} // anonymous namespace
+
void CPU::Initialize() {
#if defined(ARCH_CPU_X86_FAMILY)
int cpu_info[4] = {-1};
@@ -113,14 +114,25 @@ void CPU::Initialize() {
type_ = (cpu_info[0] >> 12) & 0x3;
ext_model_ = (cpu_info[0] >> 16) & 0xf;
ext_family_ = (cpu_info[0] >> 20) & 0xff;
- has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
- has_sse_ = (cpu_info[3] & 0x02000000) != 0;
- has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
- has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
+ has_mmx_ = (cpu_info[3] & 0x00800000) != 0;
+ has_sse_ = (cpu_info[3] & 0x02000000) != 0;
+ has_sse2_ = (cpu_info[3] & 0x04000000) != 0;
+ has_sse3_ = (cpu_info[2] & 0x00000001) != 0;
has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
- has_avx_ = (cpu_info[2] & 0x10000000) != 0;
+ has_avx_hardware_ =
+ (cpu_info[2] & 0x10000000) != 0;
+ // AVX instructions will generate an illegal instruction exception unless
+ // a) they are supported by the CPU,
+ // 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
+ has_avx_ =
+ has_avx_hardware_ &&
+ (cpu_info[2] & 0x08000000) != 0 /* OSXSAVE */ &&
+ (_xgetbv(0) & 6) == 6 /* XSAVE enabled by kernel */;
+ has_aesni_ = (cpu_info[2] & 0x02000000) != 0;
}
// Get the brand string of the cpu.
@@ -145,6 +157,13 @@ 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");
#endif
}
diff --git a/chromium/base/cpu.h b/chromium/base/cpu.h
index 509763e170f..595ed97b7ce 100644
--- a/chromium/base/cpu.h
+++ b/chromium/base/cpu.h
@@ -46,6 +46,13 @@ class BASE_EXPORT CPU {
bool has_sse41() const { return has_sse41_; }
bool has_sse42() const { return has_sse42_; }
bool has_avx() const { return has_avx_; }
+ // has_avx_hardware returns true when AVX is present in the CPU. This might
+ // differ from the value of |has_avx()| because |has_avx()| also tests for
+ // operating system support needed to actually call AVX instuctions.
+ // Note: you should never need to call this function. It was added in order
+ // to workaround a bug in NSS but |has_avx()| is what you want.
+ bool has_avx_hardware() const { return has_avx_hardware_; }
+ bool has_aesni() const { return has_aesni_; }
bool has_non_stop_time_stamp_counter() const {
return has_non_stop_time_stamp_counter_;
}
@@ -71,6 +78,8 @@ class BASE_EXPORT CPU {
bool has_sse41_;
bool has_sse42_;
bool has_avx_;
+ bool has_avx_hardware_;
+ bool has_aesni_;
bool has_non_stop_time_stamp_counter_;
std::string cpu_vendor_;
std::string cpu_brand_;
diff --git a/chromium/base/debug/crash_logging.cc b/chromium/base/debug/crash_logging.cc
index 6a36a916c62..caf10b49b07 100644
--- a/chromium/base/debug/crash_logging.cc
+++ b/chromium/base/debug/crash_logging.cc
@@ -46,14 +46,13 @@ const size_t kLargestValueAllowed = 1024;
void SetCrashKeyValue(const base::StringPiece& key,
const base::StringPiece& value) {
- if (!g_set_key_func_)
+ if (!g_set_key_func_ || !g_crash_keys_)
return;
const CrashKey* crash_key = LookupCrashKey(key);
- // TODO(rsesek): Do this:
- //DCHECK(crash_key) << "All crash keys must be registered before use "
- // << "(key = " << key << ")";
+ DCHECK(crash_key) << "All crash keys must be registered before use "
+ << "(key = " << key << ")";
// Handle the un-chunked case.
if (!crash_key || crash_key->max_length <= g_chunk_max_length_) {
@@ -78,7 +77,7 @@ void SetCrashKeyValue(const base::StringPiece& key,
}
void ClearCrashKey(const base::StringPiece& key) {
- if (!g_clear_key_func_)
+ if (!g_clear_key_func_ || !g_crash_keys_)
return;
const CrashKey* crash_key = LookupCrashKey(key);
@@ -163,6 +162,8 @@ size_t InitCrashKeys(const CrashKey* const keys, size_t count,
}
const CrashKey* LookupCrashKey(const base::StringPiece& key) {
+ if (!g_crash_keys_)
+ return NULL;
CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string());
if (it == g_crash_keys_->end())
return NULL;
diff --git a/chromium/base/debug/debugger_posix.cc b/chromium/base/debug/debugger_posix.cc
index 066c592bd45..60ad5218308 100644
--- a/chromium/base/debug/debugger_posix.cc
+++ b/chromium/base/debug/debugger_posix.cc
@@ -157,7 +157,7 @@ bool BeingDebugged() {
char buf[1024];
ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf)));
- if (HANDLE_EINTR(close(status_fd)) < 0)
+ if (IGNORE_EINTR(close(status_fd)) < 0)
return false;
if (num_read <= 0)
@@ -195,9 +195,18 @@ bool BeingDebugged() {
// +-------+-----------------+-----------------+
//
// Thus we do the following:
-// Linux: Debug mode, send SIGTRAP; Release mode, send SIGABRT.
+// Linux: Debug mode if a debugger is attached, send SIGTRAP; otherwise send
+// SIGABRT
// Mac: Always send SIGTRAP.
+#if defined(ARCH_CPU_ARM_FAMILY)
+#define DEBUG_BREAK_ASM() asm("bkpt 0")
+#elif defined(ARCH_CPU_MIPS_FAMILY)
+#define DEBUG_BREAK_ASM() asm("break 2")
+#elif defined(ARCH_CPU_X86_FAMILY)
+#define DEBUG_BREAK_ASM() asm("int3")
+#endif
+
#if defined(NDEBUG) && !defined(OS_MACOSX) && !defined(OS_ANDROID)
#define DEBUG_BREAK() abort()
#elif defined(OS_NACL)
@@ -205,7 +214,7 @@ bool BeingDebugged() {
// should ask for advice from some NaCl experts about the optimum thing here.
// http://code.google.com/p/nativeclient/issues/detail?id=645
#define DEBUG_BREAK() abort()
-#elif defined(OS_ANDROID)
+#elif !defined(OS_MACOSX)
// Though Android has a "helpful" process called debuggerd to catch native
// signals on the general assumption that they are fatal errors. If no debugger
// is attached, we call abort since Breakpad needs SIGABRT to create a dump.
@@ -214,13 +223,17 @@ bool BeingDebugged() {
// difficulty continuing in a debugger once we stop from SIG triggered by native
// code, use GDB to set |go| to 1 to resume execution; for X86 platform, use
// "int3" to setup breakpiont and raise SIGTRAP.
+//
+// On other POSIX architectures, except Mac OS X, we use the same logic to
+// ensure that breakpad creates a dump on crashes while it is still possible to
+// use a debugger.
namespace {
void DebugBreak() {
if (!BeingDebugged()) {
abort();
} else {
-#if defined(ARCH_CPU_X86_FAMILY)
- asm("int3");
+#if defined(DEBUG_BREAK_ASM)
+ DEBUG_BREAK_ASM();
#else
volatile int go = 0;
while (!go) {
@@ -231,13 +244,10 @@ void DebugBreak() {
}
} // namespace
#define DEBUG_BREAK() DebugBreak()
-#elif defined(ARCH_CPU_ARM_FAMILY)
-// ARM && !ANDROID
-#define DEBUG_BREAK() asm("bkpt 0")
-#elif defined(ARCH_CPU_MIPS_FAMILY)
-#define DEBUG_BREAK() asm("break 2")
+#elif defined(DEBUG_BREAK_ASM)
+#define DEBUG_BREAK() DEBUG_BREAK_ASM()
#else
-#define DEBUG_BREAK() asm("int3")
+#error "Don't know how to debug break on this architecture/OS"
#endif
void BreakDebugger() {
diff --git a/chromium/base/debug/leak_annotations.h b/chromium/base/debug/leak_annotations.h
index 86e8ea911ce..27fe663bddb 100644
--- a/chromium/base/debug/leak_annotations.h
+++ b/chromium/base/debug/leak_annotations.h
@@ -5,40 +5,30 @@
#ifndef BASE_DEBUG_LEAK_ANNOTATIONS_H_
#define BASE_DEBUG_LEAK_ANNOTATIONS_H_
+#include "base/basictypes.h"
#include "build/build_config.h"
// This file defines macros which can be used to annotate intentional memory
-// leaks. Support for annotations is implemented in HeapChecker and
-// LeakSanitizer. Annotated objects will be treated as a source of live
-// pointers, i.e. any heap objects reachable by following pointers from an
-// annotated object will not be reported as leaks.
+// leaks. Support for annotations is implemented in LeakSanitizer. Annotated
+// objects will be treated as a source of live pointers, i.e. any heap objects
+// reachable by following pointers from an annotated object will not be
+// reported as leaks.
//
// ANNOTATE_SCOPED_MEMORY_LEAK: all allocations made in the current scope
// will be annotated as leaks.
// ANNOTATE_LEAKING_OBJECT_PTR(X): the heap object referenced by pointer X will
// be annotated as a leak.
-//
-// Note that HeapChecker will report a fatal error if an object which has been
-// annotated with ANNOTATE_LEAKING_OBJECT_PTR is later deleted (but
-// LeakSanitizer won't).
-
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_NACL) && \
- defined(USE_HEAPCHECKER)
-#include "third_party/tcmalloc/chromium/src/gperftools/heap-checker.h"
-
-#define ANNOTATE_SCOPED_MEMORY_LEAK \
- HeapLeakChecker::Disabler heap_leak_checker_disabler; static_cast<void>(0)
-
-#define ANNOTATE_LEAKING_OBJECT_PTR(X) \
- HeapLeakChecker::IgnoreObject(X)
-
-#elif defined(LEAK_SANITIZER) && !defined(OS_NACL)
+#if defined(LEAK_SANITIZER) && !defined(OS_NACL)
+// Public LSan API from <sanitizer/lsan_interface.h>.
extern "C" {
void __lsan_disable();
void __lsan_enable();
void __lsan_ignore_object(const void *p);
+
+// Invoke leak detection immediately. If leaks are found, the process will exit.
+void __lsan_do_leak_check();
} // extern "C"
class ScopedLeakSanitizerDisabler {
diff --git a/chromium/base/debug/profiler.cc b/chromium/base/debug/profiler.cc
index 096e343e00e..ed553cdf293 100644
--- a/chromium/base/debug/profiler.cc
+++ b/chromium/base/debug/profiler.cc
@@ -14,14 +14,16 @@
#include "base/win/pe_image.h"
#endif // defined(OS_WIN)
-#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
+// TODO(peria): Enable profiling on Windows.
+#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
#include "third_party/tcmalloc/chromium/src/gperftools/profiler.h"
#endif
namespace base {
namespace debug {
-#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC)
+// TODO(peria): Enable profiling on Windows.
+#if defined(ENABLE_PROFILING) && !defined(NO_TCMALLOC) && !defined(OS_WIN)
static int profile_count = 0;
diff --git a/chromium/base/debug/stack_trace_android.cc b/chromium/base/debug/stack_trace_android.cc
index ec0508a158c..257e82309e7 100644
--- a/chromium/base/debug/stack_trace_android.cc
+++ b/chromium/base/debug/stack_trace_android.cc
@@ -26,16 +26,6 @@ struct StackCrawlState {
bool have_skipped_self;
};
-// Clang's unwind.h doesn't provide _Unwind_GetIP on ARM, refer to
-// http://llvm.org/bugs/show_bug.cgi?id=16564 for details.
-#if defined(__clang__)
-uintptr_t _Unwind_GetIP(_Unwind_Context* context) {
- uintptr_t ip = 0;
- _Unwind_VRS_Get(context, _UVRSC_CORE, 15, _UVRSD_UINT32, &ip);
- return ip & ~static_cast<uintptr_t>(0x1); // Remove thumb mode bit.
-}
-#endif
-
_Unwind_Reason_Code TraceStackFrame(_Unwind_Context* context, void* arg) {
StackCrawlState* state = static_cast<StackCrawlState*>(arg);
uintptr_t ip = _Unwind_GetIP(context);
diff --git a/chromium/base/debug/stack_trace_posix.cc b/chromium/base/debug/stack_trace_posix.cc
index eb5ee08a65e..ed1a91889b1 100644
--- a/chromium/base/debug/stack_trace_posix.cc
+++ b/chromium/base/debug/stack_trace_posix.cc
@@ -43,6 +43,7 @@ namespace {
volatile sig_atomic_t in_signal_handler = 0;
+#if !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__)
// The prefix used for mangled symbols, per the Itanium C++ ABI:
// http://www.codesourcery.com/cxx-abi/abi.html#mangling
const char kMangledSymbolPrefix[] = "_Z";
@@ -51,6 +52,7 @@ const char kMangledSymbolPrefix[] = "_Z";
// (('a'..'z').to_a+('A'..'Z').to_a+('0'..'9').to_a + ['_']).join
const char kSymbolCharacters[] =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
+#endif // !defined(USE_SYMBOLIZE) && defined(__GLIBCXX__)
#if !defined(USE_SYMBOLIZE)
// Demangles C++ symbols in the given text. Example:
@@ -177,6 +179,7 @@ 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.
@@ -369,6 +372,7 @@ 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:
@@ -399,6 +403,7 @@ 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,
@@ -431,6 +436,7 @@ void WarmUpBacktrace() {
// #22 <signal handler called>
StackTrace stack_trace;
}
+#endif // !defined(OS_IOS)
} // namespace
diff --git a/chromium/base/debug/trace_event.h b/chromium/base/debug/trace_event.h
index 6b45652b0d3..18feb33f3d0 100644
--- a/chromium/base/debug/trace_event.h
+++ b/chromium/base/debug/trace_event.h
@@ -133,7 +133,7 @@
// "arg1", std::string("string will be copied"));
//
//
-// Convertible notes:
+// Convertable notes:
// Converting a large data type to a string can be costly. To help with this,
// the trace framework provides an interface ConvertableToTraceFormat. If you
// inherit from it and implement the AppendAsTraceFormat method the trace
@@ -144,17 +144,16 @@
// class MyData : public base::debug::ConvertableToTraceFormat {
// public:
// MyData() {}
-// virtual ~MyData() {}
// virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
// out->append("{\"foo\":1}");
// }
// private:
+// virtual ~MyData() {}
// DISALLOW_COPY_AND_ASSIGN(MyData);
// };
//
-// scoped_ptr<MyData> data(new MyData());
// TRACE_EVENT1("foo", "bar", "data",
-// data.PassAs<base::debug::ConvertableToTraceFormat>());
+// scoped_refptr<ConvertableToTraceFormat>(new MyData()));
//
// The trace framework will take ownership if the passed pointer and it will
// be free'd when the trace buffer is flushed.
@@ -521,12 +520,18 @@
// all match. |id| must either be a pointer or an integer value up to 64 bits.
// If it's a pointer, the bits will be xored with a hash of the process ID so
// that the same pointer on two different processes will not collide.
+//
// An asynchronous operation can consist of multiple phases. The first phase is
// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the
-// ASYNC_STEP macros. When the operation completes, call ASYNC_END.
-// An ASYNC trace typically occur on a single thread (if not, they will only be
+// ASYNC_STEP_INTO or ASYNC_STEP_PAST macros. The ASYNC_STEP_INTO macro will
+// annotate the block following the call. The ASYNC_STEP_PAST macro will
+// annotate the block prior to the call. Note that any particular event must use
+// only STEP_INTO or STEP_PAST macros; they can not mix and match. When the
+// operation completes, call ASYNC_END.
+//
+// An ASYNC trace typically occurs on a single thread (if not, they will only be
// drawn on the thread defined in the ASYNC_BEGIN event), but all events in that
-// operation must use the same |name| and |id|. Each event can have its own
+// operation must use the same |name| and |id|. Each step can have its own
// args.
#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id) \
INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_BEGIN, \
@@ -554,27 +559,34 @@
category_group, name, id, TRACE_EVENT_FLAG_COPY, \
arg1_name, arg1_val, arg2_name, arg2_val)
-// Records a single ASYNC_STEP event for |step| immediately. If the category
-// is not enabled, then this does nothing. The |name| and |id| must match the
-// ASYNC_BEGIN event above. The |step| param identifies this step within the
-// async event. This should be called at the beginning of the next phase of an
-// asynchronous operation.
-#define TRACE_EVENT_ASYNC_STEP0(category_group, name, id, step) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+// Records a single ASYNC_STEP_INTO event for |step| immediately. If the
+// category is not enabled, then this does nothing. The |name| and |id| must
+// match the ASYNC_BEGIN event above. The |step| param identifies this step
+// within the async event. This should be called at the beginning of the next
+// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any
+// ASYNC_STEP_PAST events.
+#define TRACE_EVENT_ASYNC_STEP_INTO0(category_group, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, \
category_group, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
-#define TRACE_EVENT_ASYNC_STEP1(category_group, name, id, step, \
- arg1_name, arg1_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
+#define TRACE_EVENT_ASYNC_STEP_INTO1(category_group, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_INTO, \
category_group, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
arg1_name, arg1_val)
-#define TRACE_EVENT_COPY_ASYNC_STEP0(category_group, name, id, step) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
- category_group, name, id, TRACE_EVENT_FLAG_COPY, "step", step)
-#define TRACE_EVENT_COPY_ASYNC_STEP1(category_group, name, id, step, \
- arg1_name, arg1_val) \
- INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP, \
- category_group, name, id, TRACE_EVENT_FLAG_COPY, "step", step, \
+// Records a single ASYNC_STEP_PAST event for |step| immediately. If the
+// category is not enabled, then this does nothing. The |name| and |id| must
+// match the ASYNC_BEGIN event above. The |step| param identifies this step
+// within the async event. This should be called at the beginning of the next
+// phase of an asynchronous operation. The ASYNC_BEGIN event must not have any
+// ASYNC_STEP_INTO events.
+#define TRACE_EVENT_ASYNC_STEP_PAST0(category_group, name, id, step) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, \
+ category_group, name, id, TRACE_EVENT_FLAG_NONE, "step", step)
+#define TRACE_EVENT_ASYNC_STEP_PAST1(category_group, name, id, step, \
+ arg1_name, arg1_val) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID(TRACE_EVENT_PHASE_ASYNC_STEP_PAST, \
+ category_group, name, id, TRACE_EVENT_FLAG_NONE, "step", step, \
arg1_name, arg1_val)
// Records a single ASYNC_END event for "name" immediately. If the category
@@ -718,7 +730,7 @@
#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
*ret = true; \
} else { \
*ret = false; \
@@ -764,7 +776,7 @@
base::debug::TraceLog::GetInstance()->GetNumTracesRecorded
// Add a trace event to the platform tracing system.
-// void TRACE_EVENT_API_ADD_TRACE_EVENT(
+// base::debug::TraceEventHandle TRACE_EVENT_API_ADD_TRACE_EVENT(
// char phase,
// const unsigned char* category_group_enabled,
// const char* name,
@@ -778,7 +790,7 @@
base::debug::TraceLog::GetInstance()->AddTraceEvent
// Add a trace event to the platform tracing system.
-// void TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
+// base::debug::TraceEventHandle TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
// char phase,
// const unsigned char* category_group_enabled,
// const char* name,
@@ -793,6 +805,14 @@
#define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP \
base::debug::TraceLog::GetInstance()->AddTraceEventWithThreadIdAndTimestamp
+// Set the duration field of a COMPLETE trace event.
+// void TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(
+// const unsigned char* category_group_enabled,
+// const char* name,
+// base::debug::TraceEventHandle id)
+#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \
+ base::debug::TraceLog::GetInstance()->UpdateTraceEventDuration
+
// Defines atomic operations used internally by the tracing system.
#define TRACE_EVENT_API_ATOMIC_WORD base::subtle::AtomicWord
#define TRACE_EVENT_API_ATOMIC_LOAD(var) base::subtle::NoBarrier_Load(&(var))
@@ -825,27 +845,34 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
// No barriers are needed, because this code is designed to operate safely
// even when the unsigned char* points to garbage data (which may be the case
// on processors without cache coherency).
-#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \
- static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \
- const unsigned char* INTERNAL_TRACE_EVENT_UID(catstatic) = \
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \
+ category_group, atomic, category_group_enabled) \
+ category_group_enabled = \
reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD( \
- INTERNAL_TRACE_EVENT_UID(atomic))); \
- if (!INTERNAL_TRACE_EVENT_UID(catstatic)) { \
- INTERNAL_TRACE_EVENT_UID(catstatic) = \
+ atomic)); \
+ if (!category_group_enabled) { \
+ category_group_enabled = \
TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \
- TRACE_EVENT_API_ATOMIC_STORE(INTERNAL_TRACE_EVENT_UID(atomic), \
+ TRACE_EVENT_API_ATOMIC_STORE(atomic, \
reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>( \
- INTERNAL_TRACE_EVENT_UID(catstatic))); \
+ category_group_enabled)); \
}
+#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \
+ static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \
+ const unsigned char* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(category_group, \
+ INTERNAL_TRACE_EVENT_UID(atomic), \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled));
+
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
trace_event_internal::AddTraceEvent( \
- phase, INTERNAL_TRACE_EVENT_UID(catstatic), name, \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_internal::kNoEventId, flags, ##__VA_ARGS__); \
} \
} while (0)
@@ -855,16 +882,15 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
// ends.
#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- trace_event_internal::TraceEndOnScopeClose \
- INTERNAL_TRACE_EVENT_UID(profileScope); \
- if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
- trace_event_internal::AddTraceEvent( \
- TRACE_EVENT_PHASE_BEGIN, \
- INTERNAL_TRACE_EVENT_UID(catstatic), \
+ trace_event_internal::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
+ if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
+ base::debug::TraceEventHandle h = trace_event_internal::AddTraceEvent( \
+ TRACE_EVENT_PHASE_COMPLETE, \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
name, trace_event_internal::kNoEventId, \
TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \
- INTERNAL_TRACE_EVENT_UID(profileScope).Initialize( \
- INTERNAL_TRACE_EVENT_UID(catstatic), name); \
+ INTERNAL_TRACE_EVENT_UID(tracer).Initialize( \
+ INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, h); \
}
// Implementation detail: internal macro to create static category and add
@@ -873,12 +899,12 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
- if (*INTERNAL_TRACE_EVENT_UID(catstatic)) { \
+ if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
trace_event_internal::TraceID trace_event_trace_id( \
id, &trace_event_flags); \
trace_event_internal::AddTraceEvent( \
- phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
name, trace_event_trace_id.data(), trace_event_flags, \
##__VA_ARGS__); \
} \
@@ -890,12 +916,12 @@ 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(catstatic)) { \
+ if (*INTERNAL_TRACE_EVENT_UID(category_group_enabled)) { \
unsigned char trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
trace_event_internal::TraceID trace_event_trace_id( \
id, &trace_event_flags); \
trace_event_internal::AddTraceEventWithThreadIdAndTimestamp( \
- phase, INTERNAL_TRACE_EVENT_UID(catstatic), \
+ phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), \
name, trace_event_trace_id.data(), \
thread_id, base::TimeTicks::FromInternalValue(timestamp), \
trace_event_flags, ##__VA_ARGS__); \
@@ -910,9 +936,11 @@ TRACE_EVENT_API_CLASS_EXPORT extern \
// Phase indicates the nature of an event entry. E.g. part of a begin/end pair.
#define TRACE_EVENT_PHASE_BEGIN ('B')
#define TRACE_EVENT_PHASE_END ('E')
+#define TRACE_EVENT_PHASE_COMPLETE ('X')
#define TRACE_EVENT_PHASE_INSTANT ('i')
#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S')
-#define TRACE_EVENT_PHASE_ASYNC_STEP ('T')
+#define TRACE_EVENT_PHASE_ASYNC_STEP_INTO ('T')
+#define TRACE_EVENT_PHASE_ASYNC_STEP_PAST ('p')
#define TRACE_EVENT_PHASE_ASYNC_END ('F')
#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s')
#define TRACE_EVENT_PHASE_FLOW_STEP ('t')
@@ -1133,7 +1161,8 @@ static inline void SetTraceValue(const std::string& arg,
// pointers to the internal c_str and pass through to the tracing API,
// the arg_values must live throughout these procedures.
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1142,34 +1171,17 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
const base::TimeTicks& timestamp,
unsigned char flags,
const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val) {
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val) {
const int num_args = 1;
unsigned char arg_types[1] = { TRACE_VALUE_TYPE_CONVERTABLE };
- scoped_ptr<base::debug::ConvertableToTraceFormat> convertable_values[1];
- convertable_values[0].reset(arg1_val.release());
-
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
- num_args, &arg1_name, arg_types, NULL, convertable_values, flags);
-}
-
-static inline void AddTraceEvent(
- char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val) {
- int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
- base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags, arg1_name,
- arg1_val.Pass());
+ num_args, &arg1_name, arg_types, NULL, &arg1_val, flags);
}
template<class ARG1_TYPE>
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1178,9 +1190,9 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
const base::TimeTicks& timestamp,
unsigned char flags,
const char* arg1_name,
- ARG1_TYPE arg1_val,
+ const ARG1_TYPE& arg1_val,
const char* arg2_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg2_val) {
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg2_val) {
const int num_args = 2;
const char* arg_names[2] = { arg1_name, arg2_name };
@@ -1189,35 +1201,17 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
arg_types[1] = TRACE_VALUE_TYPE_CONVERTABLE;
- scoped_ptr<base::debug::ConvertableToTraceFormat> convertable_values[2];
- convertable_values[1].reset(arg2_val.release());
+ scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2];
+ convertable_values[1] = arg2_val;
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
num_args, arg_names, arg_types, arg_values, convertable_values, flags);
}
-template<class ARG1_TYPE>
-static inline void AddTraceEvent(
- char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- ARG1_TYPE arg1_val,
- const char* arg2_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg2_val) {
- int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
- base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags,
- arg1_name, arg1_val,
- arg2_name, arg2_val.Pass());
-}
-
template<class ARG2_TYPE>
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1226,9 +1220,9 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
const base::TimeTicks& timestamp,
unsigned char flags,
const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val,
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val,
const char* arg2_name,
- ARG2_TYPE arg2_val) {
+ const ARG2_TYPE& arg2_val) {
const int num_args = 2;
const char* arg_names[2] = { arg1_name, arg2_name };
@@ -1238,34 +1232,16 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
arg_values[0] = 0;
SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]);
- scoped_ptr<base::debug::ConvertableToTraceFormat> convertable_values[2];
- convertable_values[0].reset(arg1_val.release());
+ scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2];
+ convertable_values[0] = arg1_val;
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
num_args, arg_names, arg_types, arg_values, convertable_values, flags);
}
-template<class ARG2_TYPE>
-static inline void AddTraceEvent(
- char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val,
- const char* arg2_name,
- ARG2_TYPE arg2_val) {
- int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
- base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags,
- arg1_name, arg1_val.Pass(),
- arg2_name, arg2_val);
-}
-
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1274,41 +1250,23 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
const base::TimeTicks& timestamp,
unsigned char flags,
const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val,
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg1_val,
const char* arg2_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg2_val) {
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>& arg2_val) {
const int num_args = 2;
const char* arg_names[2] = { arg1_name, arg2_name };
unsigned char arg_types[2] =
{ TRACE_VALUE_TYPE_CONVERTABLE, TRACE_VALUE_TYPE_CONVERTABLE };
- scoped_ptr<base::debug::ConvertableToTraceFormat> convertable_values[2];
- convertable_values[0].reset(arg1_val.release());
- convertable_values[1].reset(arg2_val.release());
+ scoped_refptr<base::debug::ConvertableToTraceFormat> convertable_values[2] =
+ { arg1_val, arg2_val };
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
num_args, arg_names, arg_types, NULL, convertable_values, flags);
}
-static inline void AddTraceEvent(
- char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg1_val,
- const char* arg2_name,
- scoped_ptr<base::debug::ConvertableToTraceFormat> arg2_val) {
- int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
- base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags,
- arg1_name, arg1_val.Pass(),
- arg2_name, arg2_val.Pass());
-}
-
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1316,24 +1274,26 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
int thread_id,
const base::TimeTicks& timestamp,
unsigned char flags) {
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
kZeroNumArgs, NULL, NULL, NULL, NULL, flags);
}
-static inline void AddTraceEvent(char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags) {
+static inline base::debug::TraceEventHandle AddTraceEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags);
+ return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
+ name, id, thread_id, now, flags);
}
template<class ARG1_TYPE>
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1347,28 +1307,30 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
unsigned char arg_types[1];
unsigned long long arg_values[1];
SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
num_args, &arg1_name, arg_types, arg_values, NULL, flags);
}
template<class ARG1_TYPE>
-static inline void AddTraceEvent(char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- const ARG1_TYPE& arg1_val) {
+static inline base::debug::TraceEventHandle AddTraceEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags,
+ const char* arg1_name,
+ const ARG1_TYPE& arg1_val) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags, arg1_name,
- arg1_val);
+ return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
+ name, id, thread_id, now, flags,
+ arg1_name, arg1_val);
}
template<class ARG1_TYPE, class ARG2_TYPE>
-static inline void AddTraceEventWithThreadIdAndTimestamp(
+static inline base::debug::TraceEventHandle
+AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1386,64 +1348,52 @@ static inline void AddTraceEventWithThreadIdAndTimestamp(
unsigned long long arg_values[2];
SetTraceValue(arg1_val, &arg_types[0], &arg_values[0]);
SetTraceValue(arg2_val, &arg_types[1], &arg_values[1]);
- TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
phase, category_group_enabled, name, id, thread_id, timestamp,
num_args, arg_names, arg_types, arg_values, NULL, flags);
}
template<class ARG1_TYPE, class ARG2_TYPE>
-static inline void AddTraceEvent(char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- unsigned char flags,
- const char* arg1_name,
- const ARG1_TYPE& arg1_val,
- const char* arg2_name,
- const ARG2_TYPE& arg2_val) {
+static inline base::debug::TraceEventHandle AddTraceEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ unsigned char flags,
+ const char* arg1_name,
+ const ARG1_TYPE& arg1_val,
+ const char* arg2_name,
+ const ARG2_TYPE& arg2_val) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, flags, arg1_name,
- arg1_val, arg2_name, arg2_val);
+ return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
+ name, id, thread_id, now, flags,
+ arg1_name, arg1_val,
+ arg2_name, arg2_val);
}
-// Used by TRACE_EVENTx macro. Do not use directly.
-class TRACE_EVENT_API_CLASS_EXPORT TraceEndOnScopeClose {
+// Used by TRACE_EVENTx macros. Do not use directly.
+class TRACE_EVENT_API_CLASS_EXPORT ScopedTracer {
public:
// Note: members of data_ intentionally left uninitialized. See Initialize.
- TraceEndOnScopeClose() : p_data_(NULL) {}
- ~TraceEndOnScopeClose() {
- if (p_data_)
- AddEventIfEnabled();
+ ScopedTracer() : p_data_(NULL) {}
+
+ ~ScopedTracer() {
+ if (p_data_ && *data_.category_group_enabled)
+ TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(
+ data_.category_group_enabled, data_.name, data_.event_handle);
}
void Initialize(const unsigned char* category_group_enabled,
- const char* name) {
+ const char* name,
+ base::debug::TraceEventHandle event_handle) {
data_.category_group_enabled = category_group_enabled;
data_.name = name;
+ data_.event_handle = event_handle;
p_data_ = &data_;
}
private:
- // Add the end event if the category is still enabled.
- void AddEventIfEnabled() {
- // Only called when p_data_ is non-null.
- if (*p_data_->category_group_enabled) {
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_END, // phase
- p_data_->category_group_enabled, // category enabled
- p_data_->name, // name
- kNoEventId, // id
- kZeroNumArgs, // num_args
- NULL, // arg_names
- NULL, // arg_types
- NULL, // arg_values
- NULL, // convertable_values
- TRACE_EVENT_FLAG_NONE); // flags
- }
- }
-
// This Data struct workaround is to avoid initializing all the members
// in Data during construction of this object, since this object is always
// constructed, even when tracing is disabled. If the members of Data were
@@ -1452,36 +1402,32 @@ class TRACE_EVENT_API_CLASS_EXPORT TraceEndOnScopeClose {
struct Data {
const unsigned char* category_group_enabled;
const char* name;
+ base::debug::TraceEventHandle event_handle;
};
Data* p_data_;
Data data_;
};
// Used by TRACE_EVENT_BINARY_EFFICIENTx macro. Do not use directly.
-class TRACE_EVENT_API_CLASS_EXPORT ScopedTrace {
+class TRACE_EVENT_API_CLASS_EXPORT ScopedTraceBinaryEfficient {
public:
- ScopedTrace(TRACE_EVENT_API_ATOMIC_WORD* event_uid, const char* name);
- ~ScopedTrace();
+ ScopedTraceBinaryEfficient(const char* category_group, const char* name);
+ ~ScopedTraceBinaryEfficient();
private:
const unsigned char* category_group_enabled_;
const char* name_;
+ base::debug::TraceEventHandle event_handle_;
};
-// A support macro for TRACE_EVENT_BINARY_EFFICIENTx
-#define INTERNAL_TRACE_EVENT_BINARY_EFFICIENT_ADD_SCOPED( \
- category_group, name, ...) \
- static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \
- trace_event_internal::ScopedTrace \
- INTERNAL_TRACE_EVENT_UID(profileScope)( \
- &INTERNAL_TRACE_EVENT_UID(atomic), name); \
-
// This macro generates less code then TRACE_EVENT0 but is also
// slower to execute when tracing is off. It should generally only be
// used with code that is seldom executed or conditionally executed
// when debugging.
+// For now the category_group must be "gpu".
#define TRACE_EVENT_BINARY_EFFICIENT0(category_group, name) \
- INTERNAL_TRACE_EVENT_BINARY_EFFICIENT_ADD_SCOPED(category_group, name)
+ trace_event_internal::ScopedTraceBinaryEfficient \
+ INTERNAL_TRACE_EVENT_UID(scoped_trace)(category_group, name);
// TraceEventSamplingStateScope records the current sampling state
// and sets a new sampling state. When the scope exists, it restores
diff --git a/chromium/base/debug/trace_event_android.cc b/chromium/base/debug/trace_event_android.cc
index 78d9de66747..567c48eb293 100644
--- a/chromium/base/debug/trace_event_android.cc
+++ b/chromium/base/debug/trace_event_android.cc
@@ -10,6 +10,7 @@
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
namespace {
@@ -21,18 +22,18 @@ void WriteEvent(
const char* category_group,
const char* name,
unsigned long long id,
- int num_args,
const char** arg_names,
const unsigned char* arg_types,
- const unsigned long long* arg_values,
- scoped_ptr<base::debug::ConvertableToTraceFormat> convertable_values[],
+ const base::debug::TraceEvent::TraceValue* arg_values,
+ const scoped_refptr<base::debug::ConvertableToTraceFormat>*
+ convertable_values,
unsigned char flags) {
std::string out = base::StringPrintf("%c|%d|%s", phase, getpid(), name);
if (flags & TRACE_EVENT_FLAG_HAS_ID)
base::StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id));
out += '|';
- for (int i = 0; i < num_args; ++i) {
+ for (int i = 0; i < base::debug::kTraceMaxNumArgs && arg_names[i]; ++i) {
if (i)
out += ';';
out += arg_names[i];
@@ -41,9 +42,8 @@ void WriteEvent(
if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
convertable_values[i]->AppendAsTraceFormat(&out);
} else {
- base::debug::TraceEvent::TraceValue value;
- value.as_uint = arg_values[i];
- base::debug::TraceEvent::AppendValueAsJSON(arg_types[i], value, &out);
+ base::debug::TraceEvent::AppendValueAsJSON(
+ arg_types[i], arg_values[i], &out);
}
// Remove the quotes which may confuse the atrace script.
ReplaceSubstringsAfterOffset(&out, value_start, "\\\"", "'");
@@ -58,78 +58,112 @@ void WriteEvent(
write(g_atrace_fd, out.c_str(), out.size());
}
+void NoOpOutputCallback(base::WaitableEvent* complete_event,
+ const scoped_refptr<base::RefCountedString>&,
+ bool has_more_events) {
+ if (!has_more_events)
+ complete_event->Signal();
+}
+
+void EndChromeTracing(base::debug::TraceLog* trace_log,
+ base::WaitableEvent* complete_event) {
+ trace_log->SetDisabled();
+ // Delete the buffered trace events as they have been sent to atrace.
+ trace_log->Flush(base::Bind(&NoOpOutputCallback, complete_event));
+}
+
} // namespace
namespace base {
namespace debug {
+// These functions support Android systrace.py when 'webview' category is
+// traced. With the new adb_profile_chrome, we may have two phases:
+// - before WebView is ready for combined tracing, we can use adb_profile_chrome
+// to trace android categories other than 'webview' and chromium categories.
+// In this way we can avoid the conflict between StartATrace/StopATrace and
+// the intents.
+// - TODO(wangxianzhu): after WebView is ready for combined tracing, remove
+// StartATrace, StopATrace and SendToATrace, and perhaps send Java traces
+// directly to atrace in trace_event_binding.cc.
+
void TraceLog::StartATrace() {
- AutoLock lock(lock_);
+ if (g_atrace_fd != -1)
+ return;
+
+ g_atrace_fd = open(kATraceMarkerFile, O_WRONLY);
if (g_atrace_fd == -1) {
- g_atrace_fd = open(kATraceMarkerFile, O_WRONLY);
- if (g_atrace_fd == -1) {
- LOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
- } else {
- UpdateCategoryGroupEnabledFlags();
- }
+ PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
+ return;
}
+ SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString),
+ RECORD_CONTINUOUSLY);
}
void TraceLog::StopATrace() {
- AutoLock lock(lock_);
- if (g_atrace_fd != -1) {
- close(g_atrace_fd);
- g_atrace_fd = -1;
- UpdateCategoryGroupEnabledFlags();
- }
+ if (g_atrace_fd == -1)
+ return;
+
+ close(g_atrace_fd);
+ g_atrace_fd = -1;
+
+ // TraceLog::Flush() requires the current thread to have a message loop, but
+ // this thread called from Java may not have one, so flush in another thread.
+ Thread end_chrome_tracing_thread("end_chrome_tracing");
+ WaitableEvent complete_event(false, false);
+ end_chrome_tracing_thread.Start();
+ end_chrome_tracing_thread.message_loop()->PostTask(
+ FROM_HERE, base::Bind(&EndChromeTracing, Unretained(this),
+ Unretained(&complete_event)));
+ complete_event.Wait();
}
-void TraceLog::SendToATrace(
- char phase,
- const char* category_group,
- const char* name,
- unsigned long long id,
- int num_args,
- const char** arg_names,
- const unsigned char* arg_types,
- const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
- unsigned char flags) {
+void TraceEvent::SendToATrace() {
if (g_atrace_fd == -1)
return;
- switch (phase) {
+ const char* category_group =
+ TraceLog::GetCategoryGroupName(category_group_enabled_);
+
+ switch (phase_) {
case TRACE_EVENT_PHASE_BEGIN:
- WriteEvent('B', category_group, name, id,
- num_args, arg_names, arg_types, arg_values, convertable_values,
- flags);
+ WriteEvent('B', category_group, name_, id_,
+ arg_names_, arg_types_, arg_values_, convertable_values_,
+ flags_);
+ break;
+
+ case TRACE_EVENT_PHASE_COMPLETE:
+ WriteEvent(duration_.ToInternalValue() == -1 ? 'B' : 'E',
+ category_group, name_, id_,
+ arg_names_, arg_types_, arg_values_, convertable_values_,
+ flags_);
break;
case TRACE_EVENT_PHASE_END:
// Though a single 'E' is enough, here append pid, name and
// category_group etc. So that unpaired events can be found easily.
- WriteEvent('E', category_group, name, id,
- num_args, arg_names, arg_types, arg_values, convertable_values,
- flags);
+ WriteEvent('E', category_group, name_, id_,
+ arg_names_, arg_types_, arg_values_, convertable_values_,
+ flags_);
break;
case TRACE_EVENT_PHASE_INSTANT:
// Simulate an instance event with a pair of begin/end events.
- WriteEvent('B', category_group, name, id,
- num_args, arg_names, arg_types, arg_values, convertable_values,
- flags);
+ WriteEvent('B', category_group, name_, id_,
+ arg_names_, arg_types_, arg_values_, convertable_values_,
+ flags_);
write(g_atrace_fd, "E", 1);
break;
case TRACE_EVENT_PHASE_COUNTER:
- for (int i = 0; i < num_args; ++i) {
- DCHECK(arg_types[i] == TRACE_VALUE_TYPE_INT);
- std::string out = base::StringPrintf("C|%d|%s-%s",
- getpid(), name, arg_names[i]);
- if (flags & TRACE_EVENT_FLAG_HAS_ID)
- StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id));
+ for (int i = 0; i < kTraceMaxNumArgs && arg_names_[i]; ++i) {
+ DCHECK(arg_types_[i] == TRACE_VALUE_TYPE_INT);
+ std::string out = base::StringPrintf(
+ "C|%d|%s-%s", getpid(), name_, arg_names_[i]);
+ if (flags_ & TRACE_EVENT_FLAG_HAS_ID)
+ StringAppendF(&out, "-%" PRIx64, static_cast<uint64>(id_));
StringAppendF(&out, "|%d|%s",
- static_cast<int>(arg_values[i]), category_group);
+ static_cast<int>(arg_values_[i].as_int), category_group);
write(g_atrace_fd, out.c_str(), out.size());
}
break;
@@ -140,18 +174,24 @@ void TraceLog::SendToATrace(
}
}
-// Must be called with lock_ locked.
-void TraceLog::ApplyATraceEnabledFlag(unsigned char* category_group_enabled) {
- if (g_atrace_fd == -1)
- return;
-
- // Don't enable disabled-by-default categories for atrace.
- const char* category_group = GetCategoryGroupName(category_group_enabled);
- if (strncmp(category_group, TRACE_DISABLED_BY_DEFAULT(""),
- strlen(TRACE_DISABLED_BY_DEFAULT(""))) == 0)
+void TraceLog::AddClockSyncMetadataEvent() {
+ int atrace_fd = open(kATraceMarkerFile, O_WRONLY | O_APPEND);
+ if (atrace_fd == -1) {
+ PLOG(WARNING) << "Couldn't open " << kATraceMarkerFile;
return;
+ }
- *category_group_enabled |= ATRACE_ENABLED;
+ // Android's kernel trace system has a trace_marker feature: this is a file on
+ // debugfs that takes the written data and pushes it onto the trace
+ // buffer. So, to establish clock sync, we write our monotonic clock into that
+ // trace buffer.
+ TimeTicks now = TimeTicks::NowFromSystemTraceTime();
+ double now_in_seconds = now.ToInternalValue() / 1000000.0;
+ std::string marker = StringPrintf(
+ "trace_event_clock_sync: parent_ts=%f\n", now_in_seconds);
+ if (write(atrace_fd, marker.c_str(), marker.size()) == -1)
+ PLOG(WARNING) << "Couldn't write to " << kATraceMarkerFile;
+ close(atrace_fd);
}
} // namespace debug
diff --git a/chromium/base/debug/trace_event_impl.cc b/chromium/base/debug/trace_event_impl.cc
index fd683501efd..e774f621e04 100644
--- a/chromium/base/debug/trace_event_impl.cc
+++ b/chromium/base/debug/trace_event_impl.cc
@@ -12,11 +12,13 @@
#include "base/debug/leak_annotations.h"
#include "base/debug/trace_event.h"
#include "base/format_macros.h"
+#include "base/json/string_escape.h"
#include "base/lazy_instance.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "base/process/process_metrics.h"
#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
@@ -56,13 +58,19 @@ const int kOverheadReportThresholdInMicroseconds = 50;
// Controls the number of trace events we will buffer in-memory
// before throwing them away.
-const size_t kTraceEventVectorBufferSize = 250000;
-const size_t kTraceEventRingBufferSize = kTraceEventVectorBufferSize / 4;
-const size_t kTraceEventThreadLocalBufferSize = 64;
-const size_t kTraceEventBatchSize = 1000;
-const size_t kTraceEventInitialBufferSize = 1024;
+const size_t kTraceBufferChunkSize = TraceBufferChunk::kTraceBufferChunkSize;
+const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize;
+const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4;
+const size_t kTraceEventBatchChunks = 1000 / kTraceBufferChunkSize;
+// Can store results for 30 seconds with 1 ms sampling interval.
+const size_t kMonitorTraceEventBufferChunks = 30000 / kTraceBufferChunkSize;
+// ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events.
+const size_t kEchoToConsoleTraceEventBufferChunks = 256;
-const int kThreadFlushTimeoutMs = 1000;
+const int kThreadFlushTimeoutMs = 3000;
+
+// These categories will cause deadlock when ECHO_TO_CONSOLE. crbug.com/325575.
+const char kEchoToConsoleCategoryFilter[] = "-ipc,-task";
#define MAX_CATEGORY_GROUPS 100
@@ -94,10 +102,6 @@ int g_category_index = g_num_builtin_categories; // Skip default categories.
LazyInstance<ThreadLocalPointer<const char> >::Leaky
g_current_thread_name = LAZY_INSTANCE_INITIALIZER;
-const char kRecordUntilFull[] = "record-until-full";
-const char kRecordContinuously[] = "record-continuously";
-const char kEnableSampling[] = "enable-sampling";
-
TimeTicks ThreadNow() {
return TimeTicks::IsThreadNowSupported() ?
TimeTicks::ThreadNow() : TimeTicks();
@@ -105,186 +109,347 @@ TimeTicks ThreadNow() {
class TraceBufferRingBuffer : public TraceBuffer {
public:
- TraceBufferRingBuffer()
- : unused_event_index_(0),
- oldest_event_index_(0) {
- logged_events_.reserve(kTraceEventInitialBufferSize);
+ TraceBufferRingBuffer(size_t max_chunks)
+ : max_chunks_(max_chunks),
+ recyclable_chunks_queue_(new size_t[queue_capacity()]),
+ queue_head_(0),
+ queue_tail_(max_chunks),
+ current_iteration_index_(0),
+ current_chunk_seq_(1) {
+ chunks_.reserve(max_chunks);
+ for (size_t i = 0; i < max_chunks; ++i)
+ recyclable_chunks_queue_[i] = i;
+ }
+
+ virtual scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) OVERRIDE {
+ // Because the number of threads is much less than the number of chunks,
+ // the queue should never be empty.
+ DCHECK(!QueueIsEmpty());
+
+ *index = recyclable_chunks_queue_[queue_head_];
+ queue_head_ = NextQueueIndex(queue_head_);
+ current_iteration_index_ = queue_head_;
+
+ if (*index >= chunks_.size())
+ chunks_.resize(*index + 1);
+
+ TraceBufferChunk* chunk = chunks_[*index];
+ chunks_[*index] = NULL; // Put NULL in the slot of a in-flight chunk.
+ if (chunk)
+ chunk->Reset(current_chunk_seq_++);
+ else
+ chunk = new TraceBufferChunk(current_chunk_seq_++);
+
+ return scoped_ptr<TraceBufferChunk>(chunk);
}
- virtual ~TraceBufferRingBuffer() {}
+ virtual void ReturnChunk(size_t index,
+ scoped_ptr<TraceBufferChunk> chunk) OVERRIDE {
+ // When this method is called, the queue should not be full because it
+ // can contain all chunks including the one to be returned.
+ DCHECK(!QueueIsFull());
+ DCHECK(chunk);
+ DCHECK_LT(index, chunks_.size());
+ DCHECK(!chunks_[index]);
+ chunks_[index] = chunk.release();
+ recyclable_chunks_queue_[queue_tail_] = index;
+ queue_tail_ = NextQueueIndex(queue_tail_);
+ }
- virtual void AddEvent(const TraceEvent& event) OVERRIDE {
- if (unused_event_index_ < Size())
- logged_events_[unused_event_index_] = event;
- else
- logged_events_.push_back(event);
+ virtual bool IsFull() const OVERRIDE {
+ return false;
+ }
- unused_event_index_ = NextIndex(unused_event_index_);
- if (unused_event_index_ == oldest_event_index_) {
- oldest_event_index_ = NextIndex(oldest_event_index_);
- }
+ virtual size_t Size() const OVERRIDE {
+ // This is approximate because not all of the chunks are full.
+ return chunks_.size() * kTraceBufferChunkSize;
}
- virtual bool HasMoreEvents() const OVERRIDE {
- return oldest_event_index_ != unused_event_index_;
+ virtual size_t Capacity() const OVERRIDE {
+ return max_chunks_ * kTraceBufferChunkSize;
}
- virtual const TraceEvent& NextEvent() OVERRIDE {
- DCHECK(HasMoreEvents());
+ virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) OVERRIDE {
+ if (handle.chunk_index >= chunks_.size())
+ return NULL;
+ TraceBufferChunk* chunk = chunks_[handle.chunk_index];
+ if (!chunk || chunk->seq() != handle.chunk_seq)
+ return NULL;
+ return chunk->GetEventAt(handle.event_index);
+ }
- size_t next = oldest_event_index_;
- oldest_event_index_ = NextIndex(oldest_event_index_);
- return GetEventAt(next);
+ virtual const TraceBufferChunk* NextChunk() OVERRIDE {
+ if (chunks_.empty())
+ return NULL;
+
+ while (current_iteration_index_ != queue_tail_) {
+ size_t chunk_index = recyclable_chunks_queue_[current_iteration_index_];
+ current_iteration_index_ = NextQueueIndex(current_iteration_index_);
+ if (chunk_index >= chunks_.size()) // Skip uninitialized chunks.
+ continue;
+ DCHECK(chunks_[chunk_index]);
+ return chunks_[chunk_index];
+ }
+ return NULL;
}
- virtual bool IsFull() const OVERRIDE {
- return false;
+ virtual scoped_ptr<TraceBuffer> CloneForIteration() const OVERRIDE {
+ scoped_ptr<ClonedTraceBuffer> cloned_buffer(new ClonedTraceBuffer());
+ for (size_t queue_index = queue_head_; queue_index != queue_tail_;
+ queue_index = NextQueueIndex(queue_index)) {
+ size_t chunk_index = recyclable_chunks_queue_[queue_index];
+ if (chunk_index >= chunks_.size()) // Skip uninitialized chunks.
+ continue;
+ TraceBufferChunk* chunk = chunks_[chunk_index];
+ cloned_buffer->chunks_.push_back(chunk ? chunk->Clone().release() : NULL);
+ }
+ return cloned_buffer.PassAs<TraceBuffer>();
}
- virtual size_t CountEnabledByName(
- const unsigned char* category,
- const std::string& event_name) const OVERRIDE {
- size_t notify_count = 0;
- size_t index = oldest_event_index_;
- while (index != unused_event_index_) {
- const TraceEvent& event = GetEventAt(index);
- if (category == event.category_group_enabled() &&
- strcmp(event_name.c_str(), event.name()) == 0) {
- ++notify_count;
- }
- index = NextIndex(index);
+ private:
+ class ClonedTraceBuffer : public TraceBuffer {
+ public:
+ ClonedTraceBuffer() : current_iteration_index_(0) {}
+
+ // The only implemented method.
+ virtual const TraceBufferChunk* NextChunk() OVERRIDE {
+ return current_iteration_index_ < chunks_.size() ?
+ chunks_[current_iteration_index_++] : NULL;
+ }
+
+ virtual scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) OVERRIDE {
+ NOTIMPLEMENTED();
+ return scoped_ptr<TraceBufferChunk>();
+ }
+ virtual void ReturnChunk(size_t index,
+ scoped_ptr<TraceBufferChunk>) OVERRIDE {
+ NOTIMPLEMENTED();
}
- return notify_count;
+ virtual bool IsFull() const OVERRIDE { return false; }
+ virtual size_t Size() const OVERRIDE { return 0; }
+ virtual size_t Capacity() const OVERRIDE { return 0; }
+ virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) OVERRIDE {
+ return NULL;
+ }
+ virtual scoped_ptr<TraceBuffer> CloneForIteration() const OVERRIDE {
+ NOTIMPLEMENTED();
+ return scoped_ptr<TraceBuffer>();
+ }
+
+ size_t current_iteration_index_;
+ ScopedVector<TraceBufferChunk> chunks_;
+ };
+
+ bool QueueIsEmpty() const {
+ return queue_head_ == queue_tail_;
}
- virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
- DCHECK(index < logged_events_.size());
- return logged_events_[index];
+ size_t QueueSize() const {
+ return queue_tail_ > queue_head_ ? queue_tail_ - queue_head_ :
+ queue_tail_ + queue_capacity() - queue_head_;
}
- virtual size_t Size() const OVERRIDE {
- return logged_events_.size();
+ bool QueueIsFull() const {
+ return QueueSize() == queue_capacity() - 1;
}
- virtual size_t Capacity() const OVERRIDE {
- return kTraceEventRingBufferSize;
+ size_t queue_capacity() const {
+ // One extra space to help distinguish full state and empty state.
+ return max_chunks_ + 1;
}
- private:
- static size_t NextIndex(size_t index) {
+ size_t NextQueueIndex(size_t index) const {
index++;
- if (index >= kTraceEventRingBufferSize)
+ if (index >= queue_capacity())
index = 0;
return index;
}
- size_t unused_event_index_;
- size_t oldest_event_index_;
- std::vector<TraceEvent> logged_events_;
+ size_t max_chunks_;
+ ScopedVector<TraceBufferChunk> chunks_;
+
+ scoped_ptr<size_t[]> recyclable_chunks_queue_;
+ size_t queue_head_;
+ size_t queue_tail_;
+
+ size_t current_iteration_index_;
+ uint32 current_chunk_seq_;
DISALLOW_COPY_AND_ASSIGN(TraceBufferRingBuffer);
};
class TraceBufferVector : public TraceBuffer {
public:
- TraceBufferVector() : current_iteration_index_(0) {
- logged_events_.reserve(kTraceEventInitialBufferSize);
+ TraceBufferVector()
+ : in_flight_chunk_count_(0),
+ current_iteration_index_(0) {
+ chunks_.reserve(kTraceEventVectorBufferChunks);
+ }
+
+ virtual scoped_ptr<TraceBufferChunk> GetChunk(size_t* index) OVERRIDE {
+ // This function may be called when adding normal events or indirectly from
+ // AddMetadataEventsWhileLocked(). We can not DECHECK(!IsFull()) because we
+ // have to add the metadata events and flush thread-local buffers even if
+ // the buffer is full.
+ *index = chunks_.size();
+ chunks_.push_back(NULL); // Put NULL in the slot of a in-flight chunk.
+ ++in_flight_chunk_count_;
+ // + 1 because zero chunk_seq is not allowed.
+ return scoped_ptr<TraceBufferChunk>(
+ new TraceBufferChunk(static_cast<uint32>(*index) + 1));
+ }
+
+ virtual void ReturnChunk(size_t index,
+ scoped_ptr<TraceBufferChunk> chunk) OVERRIDE {
+ DCHECK_GT(in_flight_chunk_count_, 0u);
+ DCHECK_LT(index, chunks_.size());
+ DCHECK(!chunks_[index]);
+ --in_flight_chunk_count_;
+ chunks_[index] = chunk.release();
}
- virtual ~TraceBufferVector() {
+ virtual bool IsFull() const OVERRIDE {
+ return chunks_.size() >= kTraceEventVectorBufferChunks;
}
- virtual void AddEvent(const TraceEvent& event) OVERRIDE {
- // Note, we have two callers which need to be handled:
- // - AddEventToMainBufferWhileLocked() which has two cases:
- // - called directly from AddTraceEventWithThreadIdAndTimeStamp()
- // which checks if buffer is full and does an early exit if full;
- // - called from ThreadLocalEventBuffer::FlushWhileLocked();
- // - AddThreadNameMetadataEvents().
- // We can not DECHECK(!IsFull()) because we have to add the metadata
- // events and flush thread-local buffers even if the buffer is full.
- logged_events_.push_back(event);
+ virtual size_t Size() const OVERRIDE {
+ // This is approximate because not all of the chunks are full.
+ return chunks_.size() * kTraceBufferChunkSize;
}
- virtual bool HasMoreEvents() const OVERRIDE {
- return current_iteration_index_ < Size();
+ virtual size_t Capacity() const OVERRIDE {
+ return kTraceEventVectorBufferChunks * kTraceBufferChunkSize;
}
- virtual const TraceEvent& NextEvent() OVERRIDE {
- DCHECK(HasMoreEvents());
- return GetEventAt(current_iteration_index_++);
+ virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) OVERRIDE {
+ if (handle.chunk_index >= chunks_.size())
+ return NULL;
+ TraceBufferChunk* chunk = chunks_[handle.chunk_index];
+ if (!chunk || chunk->seq() != handle.chunk_seq)
+ return NULL;
+ return chunk->GetEventAt(handle.event_index);
}
- virtual bool IsFull() const OVERRIDE {
- return Size() >= kTraceEventVectorBufferSize;
- }
-
- virtual size_t CountEnabledByName(
- const unsigned char* category,
- const std::string& event_name) const OVERRIDE {
- size_t notify_count = 0;
- for (size_t i = 0; i < Size(); i++) {
- const TraceEvent& event = GetEventAt(i);
- if (category == event.category_group_enabled() &&
- strcmp(event_name.c_str(), event.name()) == 0) {
- ++notify_count;
- }
+ virtual const TraceBufferChunk* NextChunk() OVERRIDE {
+ while (current_iteration_index_ < chunks_.size()) {
+ // Skip in-flight chunks.
+ const TraceBufferChunk* chunk = chunks_[current_iteration_index_++];
+ if (chunk)
+ return chunk;
}
- return notify_count;
- }
-
- virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
- DCHECK(index < logged_events_.size());
- return logged_events_[index];
- }
-
- virtual size_t Size() const OVERRIDE {
- return logged_events_.size();
+ return NULL;
}
- virtual size_t Capacity() const OVERRIDE {
- return kTraceEventVectorBufferSize;
+ virtual scoped_ptr<TraceBuffer> CloneForIteration() const OVERRIDE {
+ NOTIMPLEMENTED();
+ return scoped_ptr<TraceBuffer>();
}
private:
+ size_t in_flight_chunk_count_;
size_t current_iteration_index_;
- std::vector<TraceEvent> logged_events_;
+ ScopedVector<TraceBufferChunk> chunks_;
DISALLOW_COPY_AND_ASSIGN(TraceBufferVector);
};
-class TraceBufferDiscardsEvents : public TraceBuffer {
- public:
- virtual ~TraceBufferDiscardsEvents() { }
+template <typename T>
+void InitializeMetadataEvent(TraceEvent* trace_event,
+ int thread_id,
+ const char* metadata_name, const char* arg_name,
+ const T& value) {
+ if (!trace_event)
+ return;
- virtual void AddEvent(const TraceEvent& event) OVERRIDE {}
- virtual bool HasMoreEvents() const OVERRIDE { return false; }
+ int num_args = 1;
+ unsigned char arg_type;
+ unsigned long long arg_value;
+ ::trace_event_internal::SetTraceValue(value, &arg_type, &arg_value);
+ trace_event->Initialize(thread_id,
+ TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
+ &g_category_group_enabled[g_category_metadata],
+ metadata_name, ::trace_event_internal::kNoEventId,
+ num_args, &arg_name, &arg_type, &arg_value, NULL,
+ TRACE_EVENT_FLAG_NONE);
+}
- virtual const TraceEvent& NextEvent() OVERRIDE {
- NOTREACHED();
- return *static_cast<TraceEvent*>(NULL);
+class AutoThreadLocalBoolean {
+ public:
+ explicit AutoThreadLocalBoolean(ThreadLocalBoolean* thread_local_boolean)
+ : thread_local_boolean_(thread_local_boolean) {
+ DCHECK(!thread_local_boolean_->Get());
+ thread_local_boolean_->Set(true);
+ }
+ ~AutoThreadLocalBoolean() {
+ thread_local_boolean_->Set(false);
}
- virtual bool IsFull() const OVERRIDE { return false; }
+ private:
+ ThreadLocalBoolean* thread_local_boolean_;
+ DISALLOW_COPY_AND_ASSIGN(AutoThreadLocalBoolean);
+};
- virtual size_t CountEnabledByName(
- const unsigned char* category,
- const std::string& event_name) const OVERRIDE {
- return 0;
- }
+} // namespace
+
+void TraceBufferChunk::Reset(uint32 new_seq) {
+ for (size_t i = 0; i < next_free_; ++i)
+ chunk_[i].Reset();
+ next_free_ = 0;
+ seq_ = new_seq;
+}
- virtual size_t Size() const OVERRIDE { return 0; }
+TraceEvent* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
+ DCHECK(!IsFull());
+ *event_index = next_free_++;
+ return &chunk_[*event_index];
+}
- // As this buffer is never full, we can return any positive number.
- virtual size_t Capacity() const OVERRIDE { return 1; }
+scoped_ptr<TraceBufferChunk> TraceBufferChunk::Clone() const {
+ scoped_ptr<TraceBufferChunk> cloned_chunk(new TraceBufferChunk(seq_));
+ cloned_chunk->next_free_ = next_free_;
+ for (size_t i = 0; i < next_free_; ++i)
+ cloned_chunk->chunk_[i].CopyFrom(chunk_[i]);
+ return cloned_chunk.Pass();
+}
- virtual const TraceEvent& GetEventAt(size_t index) const OVERRIDE {
- NOTREACHED();
- return *static_cast<TraceEvent*>(NULL);
+// A helper class that allows the lock to be acquired in the middle of the scope
+// and unlocks at the end of scope if locked.
+class TraceLog::OptionalAutoLock {
+ public:
+ explicit OptionalAutoLock(Lock& lock)
+ : lock_(lock),
+ locked_(false) {
+ }
+
+ ~OptionalAutoLock() {
+ if (locked_)
+ lock_.Release();
+ }
+
+ void EnsureAcquired() {
+ if (!locked_) {
+ lock_.Acquire();
+ locked_ = true;
+ }
}
+
+ private:
+ Lock& lock_;
+ bool locked_;
+ DISALLOW_COPY_AND_ASSIGN(OptionalAutoLock);
};
-} // namespace
+// Use this function instead of TraceEventHandle constructor to keep the
+// overhead of ScopedTracer (trace_event.h) constructor minimum.
+void MakeHandle(uint32 chunk_seq, size_t chunk_index, size_t event_index,
+ TraceEventHandle* handle) {
+ DCHECK(chunk_seq);
+ DCHECK(chunk_index < (1u << 16));
+ DCHECK(event_index < (1u << 16));
+ handle->chunk_seq = chunk_seq;
+ handle->chunk_index = static_cast<uint16>(chunk_index);
+ handle->event_index = static_cast<uint16>(event_index);
+}
////////////////////////////////////////////////////////////////////////////////
//
@@ -312,18 +477,42 @@ void CopyTraceEventParameter(char** buffer,
} // namespace
TraceEvent::TraceEvent()
- : id_(0u),
+ : duration_(TimeDelta::FromInternalValue(-1)),
+ id_(0u),
category_group_enabled_(NULL),
name_(NULL),
thread_id_(0),
phase_(TRACE_EVENT_PHASE_BEGIN),
flags_(0) {
- arg_names_[0] = NULL;
- arg_names_[1] = NULL;
+ for (int i = 0; i < kTraceMaxNumArgs; ++i)
+ arg_names_[i] = NULL;
memset(arg_values_, 0, sizeof(arg_values_));
}
-TraceEvent::TraceEvent(
+TraceEvent::~TraceEvent() {
+}
+
+void TraceEvent::CopyFrom(const TraceEvent& other) {
+ timestamp_ = other.timestamp_;
+ thread_timestamp_ = other.thread_timestamp_;
+ duration_ = other.duration_;
+ id_ = other.id_;
+ category_group_enabled_ = other.category_group_enabled_;
+ name_ = other.name_;
+ thread_id_ = other.thread_id_;
+ phase_ = other.phase_;
+ flags_ = other.flags_;
+ parameter_copy_storage_ = other.parameter_copy_storage_;
+
+ for (int i = 0; i < kTraceMaxNumArgs; ++i) {
+ arg_names_[i] = other.arg_names_[i];
+ arg_types_[i] = other.arg_types_[i];
+ arg_values_[i] = other.arg_values_[i];
+ convertable_values_[i] = other.convertable_values_[i];
+ }
+}
+
+void TraceEvent::Initialize(
int thread_id,
TimeTicks timestamp,
TimeTicks thread_timestamp,
@@ -335,16 +524,17 @@ TraceEvent::TraceEvent(
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
- unsigned char flags)
- : timestamp_(timestamp),
- thread_timestamp_(thread_timestamp),
- id_(id),
- category_group_enabled_(category_group_enabled),
- name_(name),
- thread_id_(thread_id),
- phase_(phase),
- flags_(flags) {
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
+ unsigned char flags) {
+ timestamp_ = timestamp;
+ thread_timestamp_ = thread_timestamp;
+ duration_ = TimeDelta::FromInternalValue(-1);
+ id_ = id;
+ category_group_enabled_ = category_group_enabled;
+ name_ = name;
+ thread_id_ = thread_id;
+ phase_ = phase;
+ flags_ = flags;
// Clamp num_args since it may have been set by a third_party library.
num_args = (num_args > kTraceMaxNumArgs) ? kTraceMaxNumArgs : num_args;
@@ -354,14 +544,14 @@ TraceEvent::TraceEvent(
arg_types_[i] = arg_types[i];
if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE)
- convertable_values_[i].reset(convertable_values[i].release());
+ convertable_values_[i] = convertable_values[i];
else
arg_values_[i].as_uint = arg_values[i];
}
for (; i < kTraceMaxNumArgs; ++i) {
arg_names_[i] = NULL;
arg_values_[i].as_uint = 0u;
- convertable_values_[i].reset();
+ convertable_values_[i] = NULL;
arg_types_[i] = TRACE_VALUE_TYPE_UINT;
}
@@ -409,68 +599,26 @@ TraceEvent::TraceEvent(
}
}
-TraceEvent::TraceEvent(const TraceEvent& other)
- : timestamp_(other.timestamp_),
- thread_timestamp_(other.thread_timestamp_),
- id_(other.id_),
- category_group_enabled_(other.category_group_enabled_),
- name_(other.name_),
- thread_id_(other.thread_id_),
- phase_(other.phase_),
- flags_(other.flags_) {
- parameter_copy_storage_ = other.parameter_copy_storage_;
-
- for (int i = 0; i < kTraceMaxNumArgs; ++i) {
- arg_values_[i] = other.arg_values_[i];
- arg_names_[i] = other.arg_names_[i];
- arg_types_[i] = other.arg_types_[i];
-
- if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
- convertable_values_[i].reset(
- const_cast<TraceEvent*>(&other)->convertable_values_[i].release());
- } else {
- convertable_values_[i].reset();
- }
- }
+void TraceEvent::Reset() {
+ // Only reset fields that won't be initialized in Initialize(), or that may
+ // hold references to other objects.
+ duration_ = TimeDelta::FromInternalValue(-1);
+ parameter_copy_storage_ = NULL;
+ for (int i = 0; i < kTraceMaxNumArgs; ++i)
+ convertable_values_[i] = NULL;
}
-TraceEvent& TraceEvent::operator=(const TraceEvent& other) {
- if (this == &other)
- return *this;
-
- timestamp_ = other.timestamp_;
- thread_timestamp_ = other.thread_timestamp_;
- id_ = other.id_;
- category_group_enabled_ = other.category_group_enabled_;
- name_ = other.name_;
- parameter_copy_storage_ = other.parameter_copy_storage_;
- thread_id_ = other.thread_id_;
- phase_ = other.phase_;
- flags_ = other.flags_;
-
- for (int i = 0; i < kTraceMaxNumArgs; ++i) {
- arg_values_[i] = other.arg_values_[i];
- arg_names_[i] = other.arg_names_[i];
- arg_types_[i] = other.arg_types_[i];
-
- if (arg_types_[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
- convertable_values_[i].reset(
- const_cast<TraceEvent*>(&other)->convertable_values_[i].release());
- } else {
- convertable_values_[i].reset();
- }
- }
- return *this;
-}
-
-TraceEvent::~TraceEvent() {
+void TraceEvent::UpdateDuration(const TimeTicks& now,
+ const TimeTicks& thread_now) {
+ DCHECK(duration_.ToInternalValue() == -1);
+ duration_ = now - timestamp_;
+ thread_duration_ = thread_now - thread_timestamp_;
}
// static
void TraceEvent::AppendValueAsJSON(unsigned char type,
TraceEvent::TraceValue value,
std::string* out) {
- std::string::size_type start_pos;
switch (type) {
case TRACE_VALUE_TYPE_BOOL:
*out += value.as_bool ? "true" : "false";
@@ -481,9 +629,30 @@ void TraceEvent::AppendValueAsJSON(unsigned char type,
case TRACE_VALUE_TYPE_INT:
StringAppendF(out, "%" PRId64, static_cast<int64>(value.as_int));
break;
- case TRACE_VALUE_TYPE_DOUBLE:
- StringAppendF(out, "%f", value.as_double);
+ 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");
+ }
+
+ StringAppendF(out, "%s", real.c_str());
break;
+ }
case TRACE_VALUE_TYPE_POINTER:
// JSON only supports double and int numbers.
// So as not to lose bits from a 64-bit pointer, output as a hex string.
@@ -493,17 +662,7 @@ void TraceEvent::AppendValueAsJSON(unsigned char type,
break;
case TRACE_VALUE_TYPE_STRING:
case TRACE_VALUE_TYPE_COPY_STRING:
- *out += "\"";
- start_pos = out->size();
- *out += value.as_string ? value.as_string : "NULL";
- // insert backslash before special characters for proper json format.
- while ((start_pos = out->find_first_of("\\\"", start_pos)) !=
- std::string::npos) {
- out->insert(start_pos, 1, '\\');
- // skip inserted escape character and following character.
- start_pos += 2;
- }
- *out += "\"";
+ EscapeJSONString(value.as_string ? value.as_string : "NULL", true, out);
break;
default:
NOTREACHED() << "Don't know how to print this value";
@@ -541,6 +700,17 @@ void TraceEvent::AppendAsJSON(std::string* out) const {
}
*out += "}";
+ if (phase_ == TRACE_EVENT_PHASE_COMPLETE) {
+ int64 duration = duration_.ToInternalValue();
+ if (duration != -1)
+ StringAppendF(out, ",\"dur\":%" PRId64, duration);
+ if (!thread_timestamp_.is_null()) {
+ int64 thread_duration = thread_duration_.ToInternalValue();
+ if (thread_duration != -1)
+ StringAppendF(out, ",\"tdur\":%" PRId64, thread_duration);
+ }
+ }
+
// Output tts if thread_timestamp is valid.
if (!thread_timestamp_.is_null()) {
int64 thread_time_int64 = thread_timestamp_.ToInternalValue();
@@ -669,10 +839,10 @@ class TraceSamplingThread : public PlatformThread::Delegate {
// Implementation of PlatformThread::Delegate:
virtual void ThreadMain() OVERRIDE;
- static void DefaultSampleCallback(TraceBucketData* bucekt_data);
+ static void DefaultSamplingCallback(TraceBucketData* bucekt_data);
void Stop();
- void InstallWaitableEventForSamplingTesting(WaitableEvent* waitable_event);
+ void WaitSamplingEventForTesting();
private:
friend class TraceLog;
@@ -689,14 +859,14 @@ class TraceSamplingThread : public PlatformThread::Delegate {
const char** name);
std::vector<TraceBucketData> sample_buckets_;
bool thread_running_;
- scoped_ptr<CancellationFlag> cancellation_flag_;
- scoped_ptr<WaitableEvent> waitable_event_for_testing_;
+ CancellationFlag cancellation_flag_;
+ WaitableEvent waitable_event_for_testing_;
};
TraceSamplingThread::TraceSamplingThread()
- : thread_running_(false) {
- cancellation_flag_.reset(new CancellationFlag);
+ : thread_running_(false),
+ waitable_event_for_testing_(false, false) {
}
TraceSamplingThread::~TraceSamplingThread() {
@@ -706,17 +876,17 @@ void TraceSamplingThread::ThreadMain() {
PlatformThread::SetName("Sampling Thread");
thread_running_ = true;
const int kSamplingFrequencyMicroseconds = 1000;
- while (!cancellation_flag_->IsSet()) {
+ while (!cancellation_flag_.IsSet()) {
PlatformThread::Sleep(
TimeDelta::FromMicroseconds(kSamplingFrequencyMicroseconds));
GetSamples();
- if (waitable_event_for_testing_.get())
- waitable_event_for_testing_->Signal();
+ waitable_event_for_testing_.Signal();
}
}
// static
-void TraceSamplingThread::DefaultSampleCallback(TraceBucketData* bucket_data) {
+void TraceSamplingThread::DefaultSamplingCallback(
+ TraceBucketData* bucket_data) {
TRACE_EVENT_API_ATOMIC_WORD category_and_name =
TRACE_EVENT_API_ATOMIC_LOAD(*bucket_data->bucket);
if (!category_and_name)
@@ -742,6 +912,9 @@ void TraceSamplingThread::RegisterSampleBucket(
TRACE_EVENT_API_ATOMIC_WORD* bucket,
const char* const name,
TraceSampleCallback callback) {
+ // Access to sample_buckets_ doesn't cause races with the sampling thread
+ // that uses the sample_buckets_, because it is guaranteed that
+ // RegisterSampleBucket is called before the sampling thread is created.
DCHECK(!thread_running_);
sample_buckets_.push_back(TraceBucketData(bucket, name, callback));
}
@@ -755,15 +928,13 @@ void TraceSamplingThread::ExtractCategoryAndName(const char* combined,
}
void TraceSamplingThread::Stop() {
- cancellation_flag_->Set();
+ cancellation_flag_.Set();
}
-void TraceSamplingThread::InstallWaitableEventForSamplingTesting(
- WaitableEvent* waitable_event) {
- waitable_event_for_testing_.reset(waitable_event);
+void TraceSamplingThread::WaitSamplingEventForTesting() {
+ waitable_event_for_testing_.Wait();
}
-
TraceBucketData::TraceBucketData(base::subtle::AtomicWord* bucket,
const char* name,
TraceSampleCallback callback)
@@ -787,35 +958,48 @@ class TraceLog::ThreadLocalEventBuffer
ThreadLocalEventBuffer(TraceLog* trace_log);
virtual ~ThreadLocalEventBuffer();
- void AddEvent(const TraceEvent& event, NotificationHelper* notifier);
+ TraceEvent* AddTraceEvent(TraceEventHandle* handle);
+
void ReportOverhead(const TimeTicks& event_timestamp,
const TimeTicks& event_thread_timestamp);
+ TraceEvent* GetEventByHandle(TraceEventHandle handle) {
+ if (!chunk_ || handle.chunk_seq != chunk_->seq() ||
+ handle.chunk_index != chunk_index_)
+ return NULL;
+
+ return chunk_->GetEventAt(handle.event_index);
+ }
+
+ int generation() const { return generation_; }
+
private:
// MessageLoop::DestructionObserver
virtual void WillDestroyCurrentMessageLoop() OVERRIDE;
- void FlushWhileLocked(NotificationHelper* notifier);
+ void FlushWhileLocked();
- void CheckThisIsCurrentBuffer() {
+ void CheckThisIsCurrentBuffer() const {
DCHECK(trace_log_->thread_local_event_buffer_.Get() == this);
}
// Since TraceLog is a leaky singleton, trace_log_ will always be valid
// as long as the thread exists.
TraceLog* trace_log_;
- std::vector<TraceEvent> logged_events_;
+ scoped_ptr<TraceBufferChunk> chunk_;
+ size_t chunk_index_;
int event_count_;
TimeDelta overhead_;
+ int generation_;
DISALLOW_COPY_AND_ASSIGN(ThreadLocalEventBuffer);
};
TraceLog::ThreadLocalEventBuffer::ThreadLocalEventBuffer(TraceLog* trace_log)
: trace_log_(trace_log),
- event_count_(0) {
- logged_events_.reserve(kTraceEventThreadLocalBufferSize);
-
+ chunk_index_(0),
+ event_count_(0),
+ generation_(trace_log->generation()) {
// ThreadLocalEventBuffer is created only if the thread has a message loop, so
// the following message_loop won't be NULL.
MessageLoop* message_loop = MessageLoop::current();
@@ -834,67 +1018,68 @@ TraceLog::ThreadLocalEventBuffer::~ThreadLocalEventBuffer() {
// - the thread has no message loop;
// - trace_event_overhead is disabled.
if (event_count_) {
- const char* arg_names[2] = { "event_count", "average_overhead" };
- unsigned char arg_types[2];
- unsigned long long arg_values[2];
- trace_event_internal::SetTraceValue(
- event_count_, &arg_types[0], &arg_values[0]);
- trace_event_internal::SetTraceValue(
- overhead_.InMillisecondsF() / event_count_,
- &arg_types[1], &arg_values[1]);
- logged_events_.push_back(TraceEvent(
- static_cast<int>(PlatformThread::CurrentId()),
- TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
- &g_category_group_enabled[g_category_metadata],
- "trace_event_overhead", trace_event_internal::kNoEventId,
- 2, arg_names, arg_types, arg_values, NULL,
- TRACE_EVENT_FLAG_NONE));
- }
-
- NotificationHelper notifier(trace_log_);
+ InitializeMetadataEvent(AddTraceEvent(NULL),
+ static_cast<int>(base::PlatformThread::CurrentId()),
+ "overhead", "average_overhead",
+ overhead_.InMillisecondsF() / event_count_);
+ }
+
{
AutoLock lock(trace_log_->lock_);
- FlushWhileLocked(&notifier);
+ FlushWhileLocked();
trace_log_->thread_message_loops_.erase(MessageLoop::current());
}
trace_log_->thread_local_event_buffer_.Set(NULL);
- notifier.SendNotificationIfAny();
}
-void TraceLog::ThreadLocalEventBuffer::AddEvent(const TraceEvent& event,
- NotificationHelper* notifier) {
+TraceEvent* TraceLog::ThreadLocalEventBuffer::AddTraceEvent(
+ TraceEventHandle* handle) {
CheckThisIsCurrentBuffer();
- logged_events_.push_back(event);
- if (logged_events_.size() >= kTraceEventThreadLocalBufferSize) {
+
+ if (chunk_ && chunk_->IsFull()) {
+ AutoLock lock(trace_log_->lock_);
+ FlushWhileLocked();
+ chunk_.reset();
+ }
+ if (!chunk_) {
AutoLock lock(trace_log_->lock_);
- FlushWhileLocked(notifier);
+ chunk_ = trace_log_->logged_events_->GetChunk(&chunk_index_);
+ trace_log_->CheckIfBufferIsFullWhileLocked();
}
+ if (!chunk_)
+ return NULL;
+
+ size_t event_index;
+ TraceEvent* trace_event = chunk_->AddTraceEvent(&event_index);
+ if (trace_event && handle)
+ MakeHandle(chunk_->seq(), chunk_index_, event_index, handle);
+
+ return trace_event;
}
void TraceLog::ThreadLocalEventBuffer::ReportOverhead(
- const TimeTicks& event_timestamp, const TimeTicks& event_thread_timestamp) {
+ const TimeTicks& event_timestamp,
+ const TimeTicks& event_thread_timestamp) {
if (!g_category_group_enabled[g_category_trace_event_overhead])
return;
+ CheckThisIsCurrentBuffer();
+
event_count_++;
- TimeTicks now =
- TimeTicks::NowFromSystemTraceTime() - trace_log_->time_offset_;
+ TimeTicks thread_now = ThreadNow();
+ TimeTicks now = trace_log_->OffsetNow();
TimeDelta overhead = now - event_timestamp;
if (overhead.InMicroseconds() >= kOverheadReportThresholdInMicroseconds) {
- int thread_id = static_cast<int>(PlatformThread::CurrentId());
- // TODO(wangxianzhu): Use X event when it's ready.
- logged_events_.push_back(TraceEvent(
- thread_id, event_timestamp, event_thread_timestamp,
- TRACE_EVENT_PHASE_BEGIN,
- &g_category_group_enabled[g_category_trace_event_overhead],
- "overhead",
- 0, 0, NULL, NULL, NULL, NULL, 0));
- logged_events_.push_back(TraceEvent(
- thread_id, now, ThreadNow(),
- TRACE_EVENT_PHASE_END,
- &g_category_group_enabled[g_category_trace_event_overhead],
- "overhead",
- 0, 0, NULL, NULL, NULL, NULL, 0));
+ TraceEvent* trace_event = AddTraceEvent(NULL);
+ if (trace_event) {
+ trace_event->Initialize(
+ static_cast<int>(PlatformThread::CurrentId()),
+ event_timestamp, event_thread_timestamp,
+ TRACE_EVENT_PHASE_COMPLETE,
+ &g_category_group_enabled[g_category_trace_event_overhead],
+ "overhead", 0, 0, NULL, NULL, NULL, NULL, 0);
+ trace_event->UpdateDuration(now, thread_now);
+ }
}
overhead_ += overhead;
}
@@ -903,37 +1088,17 @@ void TraceLog::ThreadLocalEventBuffer::WillDestroyCurrentMessageLoop() {
delete this;
}
-void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked(
- NotificationHelper* notifier) {
- trace_log_->lock_.AssertAcquired();
- for (size_t i = 0; i < logged_events_.size(); ++i) {
- trace_log_->AddEventToMainBufferWhileLocked(logged_events_[i]);
- }
- logged_events_.resize(0);
- trace_log_->CheckIfBufferIsFullWhileLocked(notifier);
-}
-
-TraceLog::NotificationHelper::NotificationHelper(TraceLog* trace_log)
- : trace_log_(trace_log),
- notification_(0) {
-}
-
-TraceLog::NotificationHelper::~NotificationHelper() {
-}
-
-void TraceLog::NotificationHelper::AddNotificationWhileLocked(
- int notification) {
- trace_log_->lock_.AssertAcquired();
- if (trace_log_->notification_callback_.is_null())
+void TraceLog::ThreadLocalEventBuffer::FlushWhileLocked() {
+ if (!chunk_)
return;
- if (notification_ == 0)
- callback_copy_ = trace_log_->notification_callback_;
- notification_ |= notification;
-}
-void TraceLog::NotificationHelper::SendNotificationIfAny() {
- if (notification_)
- callback_copy_.Run(notification_);
+ trace_log_->lock_.AssertAcquired();
+ if (trace_log_->CheckGeneration(generation_)) {
+ // 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
+ // find the generation mismatch and delete this buffer soon.
}
// static
@@ -941,36 +1106,9 @@ TraceLog* TraceLog::GetInstance() {
return Singleton<TraceLog, LeakySingletonTraits<TraceLog> >::get();
}
-// static
-// Note, if you add more options here you also need to update:
-// content/browser/devtools/devtools_tracing_handler:TraceOptionsFromString
-TraceLog::Options TraceLog::TraceOptionsFromString(const std::string& options) {
- std::vector<std::string> split;
- base::SplitString(options, ',', &split);
- int ret = 0;
- for (std::vector<std::string>::iterator iter = split.begin();
- iter != split.end();
- ++iter) {
- if (*iter == kRecordUntilFull) {
- ret |= RECORD_UNTIL_FULL;
- } else if (*iter == kRecordContinuously) {
- ret |= RECORD_CONTINUOUSLY;
- } else if (*iter == kEnableSampling) {
- ret |= ENABLE_SAMPLING;
- } else {
- NOTREACHED(); // Unknown option provided.
- }
- }
- if (!(ret & RECORD_UNTIL_FULL) && !(ret & RECORD_CONTINUOUSLY))
- ret |= RECORD_UNTIL_FULL; // Default when no options are specified.
-
- return static_cast<Options>(ret);
-}
-
TraceLog::TraceLog()
- : enable_count_(0),
+ : enabled_(false),
num_traces_recorded_(0),
- buffer_is_full_(0),
event_callback_(0),
dispatching_to_observer_list_(false),
process_sort_index_(0),
@@ -980,7 +1118,10 @@ TraceLog::TraceLog()
trace_options_(RECORD_UNTIL_FULL),
sampling_thread_handle_(0),
category_filter_(CategoryFilter::kDefaultCategoryFilterString),
- flush_count_(0) {
+ event_callback_category_filter_(
+ CategoryFilter::kDefaultCategoryFilterString),
+ thread_shared_chunk_index_(0),
+ generation_(0) {
// Trace is enabled or disabled on one thread while other threads are
// accessing the enabled flag. We don't care whether edge-case events are
// traced or not, so we allow races on the enabled flag to keep the trace
@@ -1001,18 +1142,22 @@ TraceLog::TraceLog()
// NaCl also shouldn't access the command line.
if (CommandLine::InitializedForCurrentProcess() &&
CommandLine::ForCurrentProcess()->HasSwitch(switches::kTraceToConsole)) {
- std::string category_string =
- CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kTraceToConsole);
-
- if (category_string.empty())
- category_string = "*";
+ std::string filter = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTraceToConsole);
+ if (filter.empty()) {
+ filter = kEchoToConsoleCategoryFilter;
+ } else {
+ filter.append(",");
+ filter.append(kEchoToConsoleCategoryFilter);
+ }
- SetEnabled(CategoryFilter(category_string), ECHO_TO_CONSOLE);
+ LOG(ERROR) << "Start " << switches::kTraceToConsole
+ << " with CategoryFilter '" << filter << "'.";
+ SetEnabled(CategoryFilter(filter), ECHO_TO_CONSOLE);
}
#endif
- logged_events_.reset(GetTraceBuffer());
+ logged_events_.reset(CreateTraceBuffer());
}
TraceLog::~TraceLog() {
@@ -1045,9 +1190,14 @@ const char* TraceLog::GetCategoryGroupName(
}
void TraceLog::UpdateCategoryGroupEnabledFlag(int category_index) {
- bool is_enabled = enable_count_ && category_filter_.IsCategoryGroupEnabled(
- g_category_groups[category_index]);
- SetCategoryGroupEnabled(category_index, is_enabled);
+ unsigned char enabled_flag = 0;
+ const char* category_group = g_category_groups[category_index];
+ if (enabled_ && category_filter_.IsCategoryGroupEnabled(category_group))
+ enabled_flag |= ENABLED_FOR_RECORDING;
+ if (event_callback_ &&
+ event_callback_category_filter_.IsCategoryGroupEnabled(category_group))
+ enabled_flag |= ENABLED_FOR_EVENT_CALLBACK;
+ g_category_group_enabled[category_index] = enabled_flag;
}
void TraceLog::UpdateCategoryGroupEnabledFlags() {
@@ -1055,22 +1205,6 @@ void TraceLog::UpdateCategoryGroupEnabledFlags() {
UpdateCategoryGroupEnabledFlag(i);
}
-void TraceLog::SetCategoryGroupEnabled(int category_index, bool is_enabled) {
- g_category_group_enabled[category_index] =
- is_enabled ? CATEGORY_GROUP_ENABLED : 0;
-
-#if defined(OS_ANDROID)
- ApplyATraceEnabledFlag(&g_category_group_enabled[category_index]);
-#endif
-}
-
-bool TraceLog::IsCategoryGroupEnabled(
- const unsigned char* category_group_enabled) {
- // On Android, ATrace and normal trace can be enabled independently.
- // This function checks if the normal trace is enabled.
- return *category_group_enabled & CATEGORY_GROUP_ENABLED;
-}
-
const unsigned char* TraceLog::GetCategoryGroupEnabledInternal(
const char* category_group) {
DCHECK(!strchr(category_group, '"')) <<
@@ -1132,7 +1266,7 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
Options old_options = trace_options();
- if (enable_count_++ > 0) {
+ if (enabled_) {
if (options != old_options) {
DLOG(ERROR) << "Attemting to re-enable tracing with a different "
<< "set of options.";
@@ -1143,37 +1277,38 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
return;
}
- if (options != old_options) {
- subtle::NoBarrier_Store(&trace_options_, options);
- logged_events_.reset(GetTraceBuffer());
- subtle::NoBarrier_Store(&buffer_is_full_, 0);
- }
-
if (dispatching_to_observer_list_) {
DLOG(ERROR) <<
"Cannot manipulate TraceLog::Enabled state from an observer.";
return;
}
+ enabled_ = true;
+
+ if (options != old_options) {
+ subtle::NoBarrier_Store(&trace_options_, options);
+ UseNextTraceBuffer();
+ }
+
num_traces_recorded_++;
category_filter_ = CategoryFilter(category_filter);
UpdateCategoryGroupEnabledFlags();
- if (options & ENABLE_SAMPLING) {
+ if ((options & ENABLE_SAMPLING) || (options & MONITOR_SAMPLING)) {
sampling_thread_.reset(new TraceSamplingThread);
sampling_thread_->RegisterSampleBucket(
&g_trace_state[0],
"bucket0",
- Bind(&TraceSamplingThread::DefaultSampleCallback));
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
sampling_thread_->RegisterSampleBucket(
&g_trace_state[1],
"bucket1",
- Bind(&TraceSamplingThread::DefaultSampleCallback));
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
sampling_thread_->RegisterSampleBucket(
&g_trace_state[2],
"bucket2",
- Bind(&TraceSamplingThread::DefaultSampleCallback));
+ Bind(&TraceSamplingThread::DefaultSamplingCallback));
if (!PlatformThread::Create(
0, sampling_thread_.get(), &sampling_thread_handle_)) {
DCHECK(false) << "failed to create thread";
@@ -1193,61 +1328,63 @@ void TraceLog::SetEnabled(const CategoryFilter& category_filter,
}
}
-const CategoryFilter& TraceLog::GetCurrentCategoryFilter() {
+CategoryFilter TraceLog::GetCurrentCategoryFilter() {
AutoLock lock(lock_);
- DCHECK(enable_count_ > 0);
return category_filter_;
}
void TraceLog::SetDisabled() {
- std::vector<EnabledStateObserver*> observer_list;
- {
- AutoLock lock(lock_);
- DCHECK(enable_count_ > 0);
+ AutoLock lock(lock_);
+ SetDisabledWhileLocked();
+}
- if (--enable_count_ != 0)
- return;
+void TraceLog::SetDisabledWhileLocked() {
+ lock_.AssertAcquired();
- if (dispatching_to_observer_list_) {
- DLOG(ERROR)
- << "Cannot manipulate TraceLog::Enabled state from an observer.";
- return;
- }
+ if (!enabled_)
+ return;
- if (sampling_thread_.get()) {
- // Stop the sampling thread.
- sampling_thread_->Stop();
- lock_.Release();
- PlatformThread::Join(sampling_thread_handle_);
- lock_.Acquire();
- sampling_thread_handle_ = PlatformThreadHandle();
- sampling_thread_.reset();
- }
+ if (dispatching_to_observer_list_) {
+ DLOG(ERROR)
+ << "Cannot manipulate TraceLog::Enabled state from an observer.";
+ return;
+ }
- category_filter_.Clear();
- subtle::NoBarrier_Store(&watch_category_, 0);
- watch_event_name_ = "";
- UpdateCategoryGroupEnabledFlags();
- AddMetadataEvents();
+ enabled_ = false;
- dispatching_to_observer_list_ = true;
- observer_list = enabled_state_observer_list_;
+ if (sampling_thread_.get()) {
+ // Stop the sampling thread.
+ sampling_thread_->Stop();
+ lock_.Release();
+ PlatformThread::Join(sampling_thread_handle_);
+ lock_.Acquire();
+ sampling_thread_handle_ = PlatformThreadHandle();
+ sampling_thread_.reset();
}
- // Dispatch to observers outside the lock in case the observer triggers a
- // trace event.
- for (size_t i = 0; i < observer_list.size(); ++i)
- observer_list[i]->OnTraceLogDisabled();
+ category_filter_.Clear();
+ subtle::NoBarrier_Store(&watch_category_, 0);
+ watch_event_name_ = "";
+ UpdateCategoryGroupEnabledFlags();
+ AddMetadataEventsWhileLocked();
+
+ dispatching_to_observer_list_ = true;
+ std::vector<EnabledStateObserver*> observer_list =
+ enabled_state_observer_list_;
{
- AutoLock lock(lock_);
- dispatching_to_observer_list_ = false;
+ // Dispatch to observers outside the lock in case the observer triggers a
+ // trace event.
+ AutoUnlock unlock(lock_);
+ for (size_t i = 0; i < observer_list.size(); ++i)
+ observer_list[i]->OnTraceLogDisabled();
}
+ dispatching_to_observer_list_ = false;
}
int TraceLog::GetNumTracesRecorded() {
AutoLock lock(lock_);
- if (enable_count_ == 0)
+ if (!enabled_)
return -1;
return num_traces_recorded_;
}
@@ -1274,47 +1411,75 @@ bool TraceLog::HasEnabledStateObserver(EnabledStateObserver* listener) const {
}
float TraceLog::GetBufferPercentFull() const {
+ AutoLock lock(lock_);
return static_cast<float>(static_cast<double>(logged_events_->Size()) /
logged_events_->Capacity());
}
-void TraceLog::SetNotificationCallback(
- const TraceLog::NotificationCallback& cb) {
+bool TraceLog::BufferIsFull() const {
AutoLock lock(lock_);
- notification_callback_ = cb;
+ return logged_events_->IsFull();
}
-TraceBuffer* TraceLog::GetTraceBuffer() {
+TraceBuffer* TraceLog::CreateTraceBuffer() {
Options options = trace_options();
if (options & RECORD_CONTINUOUSLY)
- return new TraceBufferRingBuffer();
+ return new TraceBufferRingBuffer(kTraceEventRingBufferChunks);
+ else if (options & MONITOR_SAMPLING)
+ return new TraceBufferRingBuffer(kMonitorTraceEventBufferChunks);
else if (options & ECHO_TO_CONSOLE)
- return new TraceBufferDiscardsEvents();
+ return new TraceBufferRingBuffer(kEchoToConsoleTraceEventBufferChunks);
return new TraceBufferVector();
}
-void TraceLog::AddEventToMainBufferWhileLocked(const TraceEvent& trace_event) {
- // Don't check buffer_is_full_ because we want the remaining thread-local
- // events to be flushed into the main buffer with this method, otherwise
- // we may lose some early events of a thread that generates events sparsely.
+TraceEvent* TraceLog::AddEventToThreadSharedChunkWhileLocked(
+ TraceEventHandle* handle, bool check_buffer_is_full) {
lock_.AssertAcquired();
- logged_events_->AddEvent(trace_event);
+
+ if (thread_shared_chunk_ && thread_shared_chunk_->IsFull()) {
+ logged_events_->ReturnChunk(thread_shared_chunk_index_,
+ thread_shared_chunk_.Pass());
+ }
+
+ if (!thread_shared_chunk_) {
+ thread_shared_chunk_ = logged_events_->GetChunk(
+ &thread_shared_chunk_index_);
+ if (check_buffer_is_full)
+ CheckIfBufferIsFullWhileLocked();
+ }
+ if (!thread_shared_chunk_)
+ return NULL;
+
+ size_t event_index;
+ TraceEvent* trace_event = thread_shared_chunk_->AddTraceEvent(&event_index);
+ if (trace_event && handle) {
+ MakeHandle(thread_shared_chunk_->seq(), thread_shared_chunk_index_,
+ event_index, handle);
+ }
+ return trace_event;
}
-void TraceLog::CheckIfBufferIsFullWhileLocked(NotificationHelper* notifier) {
+void TraceLog::CheckIfBufferIsFullWhileLocked() {
lock_.AssertAcquired();
- if (!subtle::NoBarrier_Load(&buffer_is_full_) && logged_events_->IsFull()) {
- subtle::NoBarrier_Store(&buffer_is_full_,
- static_cast<subtle::AtomicWord>(1));
- notifier->AddNotificationWhileLocked(TRACE_BUFFER_FULL);
- }
+ if (logged_events_->IsFull())
+ SetDisabledWhileLocked();
}
-void TraceLog::SetEventCallback(EventCallback cb) {
+void TraceLog::SetEventCallbackEnabled(const CategoryFilter& category_filter,
+ EventCallback cb) {
+ AutoLock lock(lock_);
subtle::NoBarrier_Store(&event_callback_,
reinterpret_cast<subtle::AtomicWord>(cb));
+ event_callback_category_filter_ = category_filter;
+ UpdateCategoryGroupEnabledFlags();
};
+void TraceLog::SetEventCallbackDisabled() {
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&event_callback_, 0);
+ UpdateCategoryGroupEnabledFlags();
+}
+
// Flush() works as the following:
// 1. Flush() is called in threadA whose message loop is saved in
// flush_message_loop_proxy_;
@@ -1328,8 +1493,8 @@ void TraceLog::SetEventCallback(EventCallback cb) {
void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
if (IsEnabled()) {
// Can't flush when tracing is enabled because otherwise PostTask would
- // - it generates more trace events;
- // - it deschedules the calling thread on some platforms causing inaccurate
+ // - generate more trace events;
+ // - deschedule the calling thread on some platforms causing inaccurate
// timing of the trace events.
scoped_refptr<RefCountedString> empty_result = new RefCountedString;
if (!cb.is_null())
@@ -1338,114 +1503,163 @@ void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
return;
}
- int flush_count;
+ int generation = this->generation();
{
AutoLock lock(lock_);
- flush_count = ++flush_count_;
DCHECK(!flush_message_loop_proxy_.get());
flush_message_loop_proxy_ = MessageLoopProxy::current();
DCHECK(!thread_message_loops_.size() || flush_message_loop_proxy_.get());
flush_output_callback_ = cb;
+ if (thread_shared_chunk_) {
+ logged_events_->ReturnChunk(thread_shared_chunk_index_,
+ thread_shared_chunk_.Pass());
+ }
+
if (thread_message_loops_.size()) {
for (hash_set<MessageLoop*>::const_iterator it =
thread_message_loops_.begin();
it != thread_message_loops_.end(); ++it) {
(*it)->PostTask(
FROM_HERE,
- Bind(&TraceLog::FlushCurrentThread, Unretained(this), flush_count));
+ Bind(&TraceLog::FlushCurrentThread, Unretained(this), generation));
}
flush_message_loop_proxy_->PostDelayedTask(
FROM_HERE,
- Bind(&TraceLog::OnFlushTimeout, Unretained(this), flush_count),
+ Bind(&TraceLog::OnFlushTimeout, Unretained(this), generation),
TimeDelta::FromMilliseconds(kThreadFlushTimeoutMs));
return;
}
}
- FinishFlush(flush_count);
+ FinishFlush(generation);
}
-void TraceLog::FinishFlush(int flush_count) {
- scoped_ptr<TraceBuffer> previous_logged_events;
- OutputCallback flush_output_callback;
-
- {
- AutoLock lock(lock_);
- if (flush_count != flush_count_)
- return;
-
- previous_logged_events.swap(logged_events_);
- logged_events_.reset(GetTraceBuffer());
- subtle::NoBarrier_Store(&buffer_is_full_, 0);
- flush_message_loop_proxy_ = NULL;
- flush_output_callback = flush_output_callback_;
- flush_output_callback_.Reset();
- }
+void TraceLog::ConvertTraceEventsToTraceFormat(
+ scoped_ptr<TraceBuffer> logged_events,
+ const TraceLog::OutputCallback& flush_output_callback) {
if (flush_output_callback.is_null())
return;
- bool has_more_events = previous_logged_events->HasMoreEvents();
// The callback need to be called at least once even if there is no events
// to let the caller know the completion of flush.
+ bool has_more_events = true;
do {
scoped_refptr<RefCountedString> json_events_str_ptr =
new RefCountedString();
- for (size_t i = 0; has_more_events && i < kTraceEventBatchSize; ++i) {
- if (i > 0)
- *(&(json_events_str_ptr->data())) += ",";
-
- previous_logged_events->NextEvent().AppendAsJSON(
- &(json_events_str_ptr->data()));
-
- has_more_events = previous_logged_events->HasMoreEvents();
+ for (size_t i = 0; i < kTraceEventBatchChunks; ++i) {
+ const TraceBufferChunk* chunk = logged_events->NextChunk();
+ if (!chunk) {
+ has_more_events = false;
+ break;
+ }
+ for (size_t j = 0; j < chunk->size(); ++j) {
+ if (i > 0 || j > 0)
+ json_events_str_ptr->data().append(",");
+ chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()));
+ }
}
flush_output_callback.Run(json_events_str_ptr, has_more_events);
} while (has_more_events);
}
+void TraceLog::FinishFlush(int generation) {
+ scoped_ptr<TraceBuffer> previous_logged_events;
+ OutputCallback flush_output_callback;
+
+ if (!CheckGeneration(generation))
+ return;
+
+ {
+ AutoLock lock(lock_);
+
+ previous_logged_events.swap(logged_events_);
+ UseNextTraceBuffer();
+ thread_message_loops_.clear();
+
+ flush_message_loop_proxy_ = NULL;
+ flush_output_callback = flush_output_callback_;
+ flush_output_callback_.Reset();
+ }
+
+ ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(),
+ flush_output_callback);
+}
+
// Run in each thread holding a local event buffer.
-void TraceLog::FlushCurrentThread(int flush_count) {
+void TraceLog::FlushCurrentThread(int generation) {
{
AutoLock lock(lock_);
- if (flush_count != flush_count_ || !flush_message_loop_proxy_) {
+ if (!CheckGeneration(generation) || !flush_message_loop_proxy_) {
// This is late. The corresponding flush has finished.
return;
}
}
+ // This will flush the thread local buffer.
delete thread_local_event_buffer_.Get();
+ AutoLock lock(lock_);
+ if (!CheckGeneration(generation) || !flush_message_loop_proxy_ ||
+ thread_message_loops_.size())
+ return;
+
+ flush_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ Bind(&TraceLog::FinishFlush, Unretained(this), generation));
+}
+
+void TraceLog::OnFlushTimeout(int generation) {
{
AutoLock lock(lock_);
- if (flush_count != flush_count_ || !flush_message_loop_proxy_ ||
- thread_message_loops_.size())
+ if (!CheckGeneration(generation) || !flush_message_loop_proxy_) {
+ // Flush has finished before timeout.
return;
+ }
- flush_message_loop_proxy_->PostTask(
- FROM_HERE,
- Bind(&TraceLog::FinishFlush, Unretained(this), flush_count));
+ LOG(WARNING) <<
+ "The following threads haven't finished flush in time. "
+ "If this happens stably for some thread, please call "
+ "TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop() from "
+ "the thread to avoid its trace events from being lost.";
+ for (hash_set<MessageLoop*>::const_iterator it =
+ thread_message_loops_.begin();
+ it != thread_message_loops_.end(); ++it) {
+ LOG(WARNING) << "Thread: " << (*it)->thread_name();
+ }
}
+ FinishFlush(generation);
}
-void TraceLog::OnFlushTimeout(int flush_count) {
+void TraceLog::FlushButLeaveBufferIntact(
+ const TraceLog::OutputCallback& flush_output_callback) {
+ scoped_ptr<TraceBuffer> previous_logged_events;
{
AutoLock lock(lock_);
- if (flush_count != flush_count_ || !flush_message_loop_proxy_) {
- // Flush has finished before timeout.
- return;
+ AddMetadataEventsWhileLocked();
+ if (thread_shared_chunk_) {
+ // Return the chunk to the main buffer to flush the sampling data.
+ logged_events_->ReturnChunk(thread_shared_chunk_index_,
+ thread_shared_chunk_.Pass());
}
+ previous_logged_events = logged_events_->CloneForIteration().Pass();
+ } // release lock
- LOG(WARNING) << thread_message_loops_.size() << " threads haven't finished"
- << " flush in time. Discarding remaining events of them";
- }
- FinishFlush(flush_count);
+ ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(),
+ flush_output_callback);
}
-void TraceLog::AddTraceEvent(
+void TraceLog::UseNextTraceBuffer() {
+ logged_events_.reset(CreateTraceBuffer());
+ subtle::NoBarrier_AtomicIncrement(&generation_, 1);
+ thread_shared_chunk_.reset();
+ thread_shared_chunk_index_ = 0;
+}
+
+TraceEventHandle TraceLog::AddTraceEvent(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1454,17 +1668,18 @@ void TraceLog::AddTraceEvent(
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
unsigned char flags) {
int thread_id = static_cast<int>(base::PlatformThread::CurrentId());
base::TimeTicks now = base::TimeTicks::NowFromSystemTraceTime();
- AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled, name, id,
- thread_id, now, num_args, arg_names,
- arg_types, arg_values,
- convertable_values, flags);
+ return AddTraceEventWithThreadIdAndTimestamp(phase, category_group_enabled,
+ name, id, thread_id, now,
+ num_args, arg_names,
+ arg_types, arg_values,
+ convertable_values, flags);
}
-void TraceLog::AddTraceEventWithThreadIdAndTimestamp(
+TraceEventHandle TraceLog::AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -1475,35 +1690,41 @@ void TraceLog::AddTraceEventWithThreadIdAndTimestamp(
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
unsigned char flags) {
+ TraceEventHandle handle = { 0, 0, 0 };
+ if (!*category_group_enabled)
+ return handle;
+
+ // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when
+ // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) ->
+ // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ...
+ if (thread_is_in_trace_event_.Get())
+ return handle;
+
+ AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_);
+
DCHECK(name);
if (flags & TRACE_EVENT_FLAG_MANGLE_ID)
id ^= process_id_hash_;
-#if defined(OS_ANDROID)
- SendToATrace(phase, GetCategoryGroupName(category_group_enabled), name, id,
- num_args, arg_names, arg_types, arg_values, convertable_values,
- flags);
-#endif
-
- if (!IsCategoryGroupEnabled(category_group_enabled))
- return;
-
- TimeTicks now = timestamp - time_offset_;
+ TimeTicks now = OffsetTimestamp(timestamp);
TimeTicks thread_now = ThreadNow();
- NotificationHelper notifier(this);
-
ThreadLocalEventBuffer* thread_local_event_buffer = NULL;
// A ThreadLocalEventBuffer needs the message loop
// - to know when the thread exits;
// - to handle the final flush.
- // For a thread without a message loop, the trace events will be added into
- // the main buffer directly.
- if (MessageLoop::current()) {
+ // For a thread without a message loop or the message loop may be blocked, the
+ // trace events will be added into the main buffer directly.
+ if (!thread_blocks_message_loop_.Get() && MessageLoop::current()) {
thread_local_event_buffer = thread_local_event_buffer_.Get();
+ if (thread_local_event_buffer &&
+ !CheckGeneration(thread_local_event_buffer->generation())) {
+ delete thread_local_event_buffer;
+ thread_local_event_buffer = NULL;
+ }
if (!thread_local_event_buffer) {
thread_local_event_buffer = new ThreadLocalEventBuffer(this);
thread_local_event_buffer_.Set(thread_local_event_buffer);
@@ -1523,7 +1744,8 @@ void TraceLog::AddTraceEventWithThreadIdAndTimestamp(
new_name && *new_name) {
g_current_thread_name.Get().Set(new_name);
- AutoLock lock(lock_);
+ AutoLock thread_info_lock(thread_info_lock_);
+
hash_map<int, std::string>::iterator existing_name =
thread_names_.find(thread_id);
if (existing_name == thread_names_.end()) {
@@ -1538,82 +1760,128 @@ void TraceLog::AddTraceEventWithThreadIdAndTimestamp(
existing_names.end(),
new_name) != existing_names.end();
if (!found) {
- existing_name->second.push_back(',');
+ if (existing_names.size())
+ existing_name->second.push_back(',');
existing_name->second.append(new_name);
}
}
}
}
- if (!subtle::NoBarrier_Load(&buffer_is_full_)) {
- TraceEvent trace_event(thread_id, now, thread_now, phase,
- category_group_enabled, name, id,
- num_args, arg_names, arg_types, arg_values,
- convertable_values, flags);
+ std::string console_message;
+ if ((*category_group_enabled & ENABLED_FOR_RECORDING)) {
+ OptionalAutoLock lock(lock_);
+ TraceEvent* trace_event = NULL;
if (thread_local_event_buffer) {
- thread_local_event_buffer->AddEvent(trace_event, &notifier);
+ trace_event = thread_local_event_buffer->AddTraceEvent(&handle);
} else {
- AutoLock lock(lock_);
- AddEventToMainBufferWhileLocked(trace_event);
- CheckIfBufferIsFullWhileLocked(&notifier);
+ lock.EnsureAcquired();
+ trace_event = AddEventToThreadSharedChunkWhileLocked(&handle, true);
}
- if (trace_options() & ECHO_TO_CONSOLE) {
- AutoLock lock(lock_);
-
- TimeDelta duration;
- if (phase == TRACE_EVENT_PHASE_END) {
- duration = timestamp - thread_event_start_times_[thread_id].top();
- thread_event_start_times_[thread_id].pop();
- }
-
- std::string thread_name = thread_names_[thread_id];
- if (thread_colors_.find(thread_name) == thread_colors_.end())
- thread_colors_[thread_name] = (thread_colors_.size() % 6) + 1;
-
- std::ostringstream log;
- log << base::StringPrintf("%s: \x1b[0;3%dm",
- thread_name.c_str(),
- thread_colors_[thread_name]);
-
- size_t depth = 0;
- if (thread_event_start_times_.find(thread_id) !=
- thread_event_start_times_.end())
- depth = thread_event_start_times_[thread_id].size();
+ if (trace_event) {
+ trace_event->Initialize(thread_id, now, thread_now, phase,
+ category_group_enabled, name, id,
+ num_args, arg_names, arg_types, arg_values,
+ convertable_values, flags);
- for (size_t i = 0; i < depth; ++i)
- log << "| ";
-
- trace_event.AppendPrettyPrinted(&log);
- if (phase == TRACE_EVENT_PHASE_END)
- log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF());
-
- LOG(ERROR) << log.str() << "\x1b[0;m";
+#if defined(OS_ANDROID)
+ trace_event->SendToATrace();
+#endif
+ }
- if (phase == TRACE_EVENT_PHASE_BEGIN)
- thread_event_start_times_[thread_id].push(timestamp);
+ if (trace_options() & ECHO_TO_CONSOLE) {
+ console_message = EventToConsoleMessage(
+ phase == TRACE_EVENT_PHASE_COMPLETE ? TRACE_EVENT_PHASE_BEGIN : phase,
+ timestamp, trace_event);
}
}
+ if (console_message.size())
+ LOG(ERROR) << console_message;
+
if (reinterpret_cast<const unsigned char*>(subtle::NoBarrier_Load(
&watch_category_)) == category_group_enabled) {
- AutoLock lock(lock_);
- if (watch_event_name_ == name)
- notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
+ bool event_name_matches;
+ WatchEventCallback watch_event_callback_copy;
+ {
+ AutoLock lock(lock_);
+ event_name_matches = watch_event_name_ == name;
+ watch_event_callback_copy = watch_event_callback_;
+ }
+ if (event_name_matches) {
+ if (!watch_event_callback_copy.is_null())
+ watch_event_callback_copy.Run();
+ }
}
- notifier.SendNotificationIfAny();
- EventCallback event_callback = reinterpret_cast<EventCallback>(
- subtle::NoBarrier_Load(&event_callback_));
- if (event_callback) {
- event_callback(phase, category_group_enabled, name, id,
- num_args, arg_names, arg_types, arg_values,
- flags);
+ if (*category_group_enabled & ENABLED_FOR_EVENT_CALLBACK) {
+ EventCallback event_callback = reinterpret_cast<EventCallback>(
+ subtle::NoBarrier_Load(&event_callback_));
+ if (event_callback) {
+ event_callback(now,
+ phase == TRACE_EVENT_PHASE_COMPLETE ?
+ TRACE_EVENT_PHASE_BEGIN : phase,
+ category_group_enabled, name, id,
+ num_args, arg_names, arg_types, arg_values,
+ flags);
+ }
}
if (thread_local_event_buffer)
thread_local_event_buffer->ReportOverhead(now, thread_now);
+
+ return handle;
+}
+
+// May be called when a COMPELETE event ends and the unfinished event has been
+// recycled (phase == TRACE_EVENT_PHASE_END and trace_event == NULL).
+std::string TraceLog::EventToConsoleMessage(unsigned char phase,
+ const TimeTicks& timestamp,
+ TraceEvent* trace_event) {
+ AutoLock thread_info_lock(thread_info_lock_);
+
+ // The caller should translate TRACE_EVENT_PHASE_COMPLETE to
+ // TRACE_EVENT_PHASE_BEGIN or TRACE_EVENT_END.
+ DCHECK(phase != TRACE_EVENT_PHASE_COMPLETE);
+
+ TimeDelta duration;
+ int thread_id = trace_event ?
+ trace_event->thread_id() : PlatformThread::CurrentId();
+ if (phase == TRACE_EVENT_PHASE_END) {
+ duration = timestamp - thread_event_start_times_[thread_id].top();
+ thread_event_start_times_[thread_id].pop();
+ }
+
+ std::string thread_name = thread_names_[thread_id];
+ if (thread_colors_.find(thread_name) == thread_colors_.end())
+ thread_colors_[thread_name] = (thread_colors_.size() % 6) + 1;
+
+ std::ostringstream log;
+ log << base::StringPrintf("%s: \x1b[0;3%dm",
+ thread_name.c_str(),
+ thread_colors_[thread_name]);
+
+ size_t depth = 0;
+ if (thread_event_start_times_.find(thread_id) !=
+ thread_event_start_times_.end())
+ depth = thread_event_start_times_[thread_id].size();
+
+ for (size_t i = 0; i < depth; ++i)
+ log << "| ";
+
+ if (trace_event)
+ trace_event->AppendPrettyPrinted(&log);
+ if (phase == TRACE_EVENT_PHASE_END)
+ log << base::StringPrintf(" (%.3f ms)", duration.InMillisecondsF());
+
+ log << "\x1b[0;m";
+
+ if (phase == TRACE_EVENT_PHASE_BEGIN)
+ thread_event_start_times_[thread_id].push(timestamp);
+
+ return log.str();
}
void TraceLog::AddTraceEventEtw(char phase,
@@ -1639,77 +1907,89 @@ void TraceLog::AddTraceEventEtw(char phase,
TRACE_EVENT_FLAG_COPY, "id", id, "extra", extra);
}
-void TraceLog::SetWatchEvent(const std::string& category_name,
- const std::string& event_name) {
- const unsigned char* category = GetCategoryGroupEnabled(
- category_name.c_str());
- size_t notify_count = 0;
- {
- AutoLock lock(lock_);
- subtle::NoBarrier_Store(&watch_category_,
- reinterpret_cast<subtle::AtomicWord>(category));
- watch_event_name_ = event_name;
+void TraceLog::UpdateTraceEventDuration(
+ const unsigned char* category_group_enabled,
+ const char* name,
+ TraceEventHandle handle) {
+ // Avoid re-entrance of AddTraceEvent. This may happen in GPU process when
+ // ECHO_TO_CONSOLE is enabled: AddTraceEvent -> LOG(ERROR) ->
+ // GpuProcessLogMessageHandler -> PostPendingTask -> TRACE_EVENT ...
+ if (thread_is_in_trace_event_.Get())
+ return;
- // First, search existing events for watch event because we want to catch
- // it even if it has already occurred.
- notify_count = logged_events_->CountEnabledByName(category, event_name);
- } // release lock
+ AutoThreadLocalBoolean thread_is_in_trace_event(&thread_is_in_trace_event_);
- // Send notification for each event found.
- for (size_t i = 0; i < notify_count; ++i) {
- NotificationHelper notifier(this);
- lock_.Acquire();
- notifier.AddNotificationWhileLocked(EVENT_WATCH_NOTIFICATION);
- lock_.Release();
- notifier.SendNotificationIfAny();
+ TimeTicks thread_now = ThreadNow();
+ TimeTicks now = OffsetNow();
+
+ std::string console_message;
+ if (*category_group_enabled & ENABLED_FOR_RECORDING) {
+ OptionalAutoLock lock(lock_);
+
+ TraceEvent* trace_event = GetEventByHandleInternal(handle, &lock);
+ if (trace_event) {
+ DCHECK(trace_event->phase() == TRACE_EVENT_PHASE_COMPLETE);
+ trace_event->UpdateDuration(now, thread_now);
+#if defined(OS_ANDROID)
+ trace_event->SendToATrace();
+#endif
+ }
+
+ if (trace_options() & ECHO_TO_CONSOLE) {
+ console_message = EventToConsoleMessage(TRACE_EVENT_PHASE_END,
+ now, trace_event);
+ }
}
+
+ if (console_message.size())
+ LOG(ERROR) << console_message;
+
+ if (*category_group_enabled & ENABLED_FOR_EVENT_CALLBACK) {
+ EventCallback event_callback = reinterpret_cast<EventCallback>(
+ subtle::NoBarrier_Load(&event_callback_));
+ if (event_callback) {
+ event_callback(now, TRACE_EVENT_PHASE_END, category_group_enabled, name,
+ trace_event_internal::kNoEventId, 0, NULL, NULL, NULL,
+ TRACE_EVENT_FLAG_NONE);
+ }
+ }
+}
+
+void TraceLog::SetWatchEvent(const std::string& category_name,
+ const std::string& event_name,
+ const WatchEventCallback& callback) {
+ const unsigned char* category = GetCategoryGroupEnabled(
+ category_name.c_str());
+ AutoLock lock(lock_);
+ subtle::NoBarrier_Store(&watch_category_,
+ reinterpret_cast<subtle::AtomicWord>(category));
+ watch_event_name_ = event_name;
+ watch_event_callback_ = callback;
}
void TraceLog::CancelWatchEvent() {
AutoLock lock(lock_);
subtle::NoBarrier_Store(&watch_category_, 0);
watch_event_name_ = "";
+ watch_event_callback_.Reset();
}
-namespace {
-
-template <typename T>
-void AddMetadataEventToBuffer(
- TraceBuffer* logged_events,
- int thread_id,
- const char* metadata_name, const char* arg_name,
- const T& value) {
- int num_args = 1;
- unsigned char arg_type;
- unsigned long long arg_value;
- trace_event_internal::SetTraceValue(value, &arg_type, &arg_value);
- logged_events->AddEvent(TraceEvent(
- thread_id,
- TimeTicks(), TimeTicks(), TRACE_EVENT_PHASE_METADATA,
- &g_category_group_enabled[g_category_metadata],
- metadata_name, trace_event_internal::kNoEventId,
- num_args, &arg_name, &arg_type, &arg_value, NULL,
- TRACE_EVENT_FLAG_NONE));
-}
-
-}
-
-void TraceLog::AddMetadataEvents() {
+void TraceLog::AddMetadataEventsWhileLocked() {
lock_.AssertAcquired();
int current_thread_id = static_cast<int>(base::PlatformThread::CurrentId());
if (process_sort_index_ != 0) {
- AddMetadataEventToBuffer(logged_events_.get(),
- current_thread_id,
- "process_sort_index", "sort_index",
- process_sort_index_);
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ current_thread_id,
+ "process_sort_index", "sort_index",
+ process_sort_index_);
}
if (process_name_.size()) {
- AddMetadataEventToBuffer(logged_events_.get(),
- current_thread_id,
- "process_name", "name",
- process_name_);
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ current_thread_id,
+ "process_name", "name",
+ process_name_);
}
if (process_labels_.size() > 0) {
@@ -1719,10 +1999,10 @@ void TraceLog::AddMetadataEvents() {
it++) {
labels.push_back(it->second);
}
- AddMetadataEventToBuffer(logged_events_.get(),
- current_thread_id,
- "process_labels", "labels",
- JoinString(labels, ','));
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ current_thread_id,
+ "process_labels", "labels",
+ JoinString(labels, ','));
}
// Thread sort indices.
@@ -1731,34 +2011,66 @@ void TraceLog::AddMetadataEvents() {
it++) {
if (it->second == 0)
continue;
- AddMetadataEventToBuffer(logged_events_.get(),
- it->first,
- "thread_sort_index", "sort_index",
- it->second);
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ it->first,
+ "thread_sort_index", "sort_index",
+ it->second);
}
// Thread names.
+ AutoLock thread_info_lock(thread_info_lock_);
for(hash_map<int, std::string>::iterator it = thread_names_.begin();
it != thread_names_.end();
it++) {
if (it->second.empty())
continue;
- AddMetadataEventToBuffer(logged_events_.get(),
- it->first,
- "thread_name", "name",
- it->second);
+ InitializeMetadataEvent(AddEventToThreadSharedChunkWhileLocked(NULL, false),
+ it->first,
+ "thread_name", "name",
+ it->second);
}
}
-void TraceLog::InstallWaitableEventForSamplingTesting(
- WaitableEvent* waitable_event) {
- sampling_thread_->InstallWaitableEventForSamplingTesting(waitable_event);
+void TraceLog::WaitSamplingEventForTesting() {
+ if (!sampling_thread_)
+ return;
+ sampling_thread_->WaitSamplingEventForTesting();
}
void TraceLog::DeleteForTesting() {
DeleteTraceLogForTesting::Delete();
}
+TraceEvent* TraceLog::GetEventByHandle(TraceEventHandle handle) {
+ return GetEventByHandleInternal(handle, NULL);
+}
+
+TraceEvent* TraceLog::GetEventByHandleInternal(TraceEventHandle handle,
+ OptionalAutoLock* lock) {
+ if (!handle.chunk_seq)
+ return NULL;
+
+ if (thread_local_event_buffer_.Get()) {
+ TraceEvent* trace_event =
+ thread_local_event_buffer_.Get()->GetEventByHandle(handle);
+ if (trace_event)
+ return trace_event;
+ }
+
+ // The event has been out-of-control of the thread local buffer.
+ // Try to get the event from the main buffer with a lock.
+ if (lock)
+ lock->EnsureAcquired();
+
+ if (thread_shared_chunk_ &&
+ handle.chunk_index == thread_shared_chunk_index_) {
+ return handle.chunk_seq == thread_shared_chunk_->seq() ?
+ thread_shared_chunk_->GetEventAt(handle.event_index) : NULL;
+ }
+
+ return logged_events_->GetEventByHandle(handle);
+}
+
void TraceLog::SetProcessID(int process_id) {
process_id_ = process_id;
// Create a FNV hash from the process ID for XORing.
@@ -1791,7 +2103,7 @@ void TraceLog::UpdateProcessLabel(
void TraceLog::RemoveProcessLabel(int label_id) {
AutoLock lock(lock_);
base::hash_map<int, std::string>::iterator it = process_labels_.find(
- label_id);
+ label_id);
if (it == process_labels_.end())
return;
@@ -1811,6 +2123,14 @@ size_t TraceLog::GetObserverCountForTest() const {
return enabled_state_observer_list_.size();
}
+void TraceLog::SetCurrentThreadBlocksMessageLoop() {
+ thread_blocks_message_loop_.Set(true);
+ if (thread_local_event_buffer_.Get()) {
+ // This will flush the thread local buffer.
+ delete thread_local_event_buffer_.Get();
+ }
+}
+
bool CategoryFilter::IsEmptyOrContainsLeadingOrTrailingWhitespace(
const std::string& str) {
return str.empty() ||
@@ -1972,49 +2292,29 @@ void CategoryFilter::Clear() {
namespace trace_event_internal {
-ScopedTrace::ScopedTrace(
- TRACE_EVENT_API_ATOMIC_WORD* event_uid, const char* name) {
- category_group_enabled_ =
- reinterpret_cast<const unsigned char*>(TRACE_EVENT_API_ATOMIC_LOAD(
- *event_uid));
- if (!category_group_enabled_) {
- category_group_enabled_ = TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("gpu");
- TRACE_EVENT_API_ATOMIC_STORE(
- *event_uid,
- reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>(category_group_enabled_));
+ScopedTraceBinaryEfficient::ScopedTraceBinaryEfficient(
+ const char* category_group, const char* name) {
+ // The single atom works because for now the category_group can only be "gpu".
+ DCHECK(strcmp(category_group, "gpu") == 0);
+ static TRACE_EVENT_API_ATOMIC_WORD atomic = 0;
+ INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES(
+ category_group, atomic, category_group_enabled_);
+ name_ = name;
+ if (*category_group_enabled_) {
+ event_handle_ =
+ TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_THREAD_ID_AND_TIMESTAMP(
+ TRACE_EVENT_PHASE_COMPLETE, category_group_enabled_, name,
+ trace_event_internal::kNoEventId,
+ static_cast<int>(base::PlatformThread::CurrentId()),
+ base::TimeTicks::NowFromSystemTraceTime(),
+ 0, NULL, NULL, NULL, NULL, TRACE_EVENT_FLAG_NONE);
}
+}
+
+ScopedTraceBinaryEfficient::~ScopedTraceBinaryEfficient() {
if (*category_group_enabled_) {
- name_ = name;
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_BEGIN, // phase
- category_group_enabled_, // category enabled
- name, // name
- 0, // id
- 0, // num_args
- NULL, // arg_names
- NULL, // arg_types
- NULL, // arg_values
- NULL, // convertable_values
- TRACE_EVENT_FLAG_NONE); // flags
- } else {
- category_group_enabled_ = NULL;
- name_ = NULL;
- }
-}
-
-ScopedTrace::~ScopedTrace() {
- if (category_group_enabled_ && *category_group_enabled_) {
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_END, // phase
- category_group_enabled_, // category enabled
- name_, // name
- 0, // id
- 0, // num_args
- NULL, // arg_names
- NULL, // arg_types
- NULL, // arg_values
- NULL, // convertable values
- TRACE_EVENT_FLAG_NONE); // flags
+ TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(category_group_enabled_,
+ name_, event_handle_);
}
}
diff --git a/chromium/base/debug/trace_event_impl.h b/chromium/base/debug/trace_event_impl.h
index 4967c10ead5..61794807a41 100644
--- a/chromium/base/debug/trace_event_impl.h
+++ b/chromium/base/debug/trace_event_impl.h
@@ -64,23 +64,29 @@ namespace debug {
// For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided
// class must implement this interface.
-class ConvertableToTraceFormat {
+class ConvertableToTraceFormat : public RefCounted<ConvertableToTraceFormat> {
public:
- virtual ~ConvertableToTraceFormat() {}
-
// Append the class info to the provided |out| string. The appended
// data must be a valid JSON object. Strings must be properly quoted, and
// escaped. There is no processing applied to the content after it is
// appended.
virtual void AppendAsTraceFormat(std::string* out) const = 0;
+
+ protected:
+ virtual ~ConvertableToTraceFormat() {}
+
+ private:
+ friend class RefCounted<ConvertableToTraceFormat>;
+};
+
+struct TraceEventHandle {
+ uint32 chunk_seq;
+ uint16 chunk_index;
+ uint16 event_index;
};
const int kTraceMaxNumArgs = 2;
-// Output records are "Events" and can be obtained via the
-// OutputCallback whenever the tracing system decides to flush. This
-// can happen at any time, on any thread, or you can programmatically
-// force it to happen.
class BASE_EXPORT TraceEvent {
public:
union TraceValue {
@@ -93,23 +99,31 @@ class BASE_EXPORT TraceEvent {
};
TraceEvent();
- TraceEvent(int thread_id,
- TimeTicks timestamp,
- TimeTicks thread_timestamp,
- char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- int num_args,
- const char** arg_names,
- const unsigned char* arg_types,
- const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
- unsigned char flags);
- TraceEvent(const TraceEvent& other);
- TraceEvent& operator=(const TraceEvent& other);
~TraceEvent();
+ // We don't need to copy TraceEvent except when TraceEventBuffer is cloned.
+ // Use explicit copy method to avoid accidentally misuse of copy.
+ void CopyFrom(const TraceEvent& other);
+
+ void Initialize(
+ int thread_id,
+ TimeTicks timestamp,
+ TimeTicks thread_timestamp,
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
+ unsigned char flags);
+
+ void Reset();
+
+ 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,
@@ -126,6 +140,10 @@ class BASE_EXPORT TraceEvent {
TimeTicks thread_timestamp() const { return thread_timestamp_; }
char phase() const { return phase_; }
int thread_id() const { return thread_id_; }
+ TimeDelta duration() const { return duration_; }
+ TimeDelta thread_duration() const { return thread_duration_; }
+ unsigned long long id() const { return id_; }
+ unsigned char flags() const { return flags_; }
// Exposed for unittesting:
@@ -139,15 +157,21 @@ class BASE_EXPORT TraceEvent {
const char* name() const { return name_; }
+#if defined(OS_ANDROID)
+ void SendToATrace();
+#endif
+
private:
// Note: these are ordered by size (largest first) for optimal packing.
TimeTicks timestamp_;
TimeTicks thread_timestamp_;
+ TimeDelta duration_;
+ TimeDelta thread_duration_;
// id_ can be used to store phase-specific data.
unsigned long long id_;
TraceValue arg_values_[kTraceMaxNumArgs];
const char* arg_names_[kTraceMaxNumArgs];
- scoped_ptr<ConvertableToTraceFormat> convertable_values_[kTraceMaxNumArgs];
+ scoped_refptr<ConvertableToTraceFormat> convertable_values_[kTraceMaxNumArgs];
const unsigned char* category_group_enabled_;
const char* name_;
scoped_refptr<base::RefCountedString> parameter_copy_storage_;
@@ -155,6 +179,43 @@ class BASE_EXPORT TraceEvent {
char phase_;
unsigned char flags_;
unsigned char arg_types_[kTraceMaxNumArgs];
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEvent);
+};
+
+// TraceBufferChunk is the basic unit of TraceBuffer.
+class BASE_EXPORT TraceBufferChunk {
+ public:
+ TraceBufferChunk(uint32 seq)
+ : next_free_(0),
+ seq_(seq) {
+ }
+
+ void Reset(uint32 new_seq);
+ TraceEvent* AddTraceEvent(size_t* event_index);
+ bool IsFull() const { return next_free_ == kTraceBufferChunkSize; }
+
+ uint32 seq() const { return seq_; }
+ size_t capacity() const { return kTraceBufferChunkSize; }
+ size_t size() const { return next_free_; }
+
+ TraceEvent* GetEventAt(size_t index) {
+ DCHECK(index < size());
+ return &chunk_[index];
+ }
+ const TraceEvent* GetEventAt(size_t index) const {
+ DCHECK(index < size());
+ return &chunk_[index];
+ }
+
+ scoped_ptr<TraceBufferChunk> Clone() const;
+
+ static const size_t kTraceBufferChunkSize = 64;
+
+ private:
+ size_t next_free_;
+ TraceEvent chunk_[kTraceBufferChunkSize];
+ uint32 seq_;
};
// TraceBuffer holds the events as they are collected.
@@ -162,15 +223,19 @@ class BASE_EXPORT TraceBuffer {
public:
virtual ~TraceBuffer() {}
- virtual void AddEvent(const TraceEvent& event) = 0;
- virtual bool HasMoreEvents() const = 0;
- virtual const TraceEvent& NextEvent() = 0;
+ virtual scoped_ptr<TraceBufferChunk> GetChunk(size_t *index) = 0;
+ virtual void ReturnChunk(size_t index,
+ scoped_ptr<TraceBufferChunk> chunk) = 0;
+
virtual bool IsFull() const = 0;
- virtual size_t CountEnabledByName(const unsigned char* category,
- const std::string& event_name) const = 0;
virtual size_t Size() const = 0;
virtual size_t Capacity() const = 0;
- virtual const TraceEvent& GetEventAt(size_t index) const = 0;
+ virtual TraceEvent* GetEventByHandle(TraceEventHandle handle) = 0;
+
+ // For iteration. Each TraceBuffer can only be iterated once.
+ virtual const TraceBufferChunk* NextChunk() = 0;
+
+ virtual scoped_ptr<TraceBuffer> CloneForIteration() const = 0;
};
// TraceResultBuffer collects and converts trace fragments returned by TraceLog
@@ -289,16 +354,6 @@ class TraceSamplingThread;
class BASE_EXPORT TraceLog {
public:
- // Notification is a mask of one or more of the following events.
- enum Notification {
- // The trace buffer does not flush dynamically, so when it fills up,
- // subsequent trace events will be dropped. This callback is generated when
- // the trace buffer is full. The callback must be thread safe.
- TRACE_BUFFER_FULL = 1 << 0,
- // A subscribed trace-event occurred.
- EVENT_WATCH_NOTIFICATION = 1 << 1
- };
-
// Options determines how the trace buffer stores data.
enum Options {
// Record until the trace buffer is full.
@@ -308,37 +363,49 @@ class BASE_EXPORT TraceLog {
// and we use it as a ring buffer during recording.
RECORD_CONTINUOUSLY = 1 << 1,
- // Enable the sampling profiler.
+ // 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 << 3,
+ ECHO_TO_CONSOLE = 1 << 4,
};
- static TraceLog* GetInstance();
+ // 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.
+ enum CategoryGroupEnabledFlags {
+ // Normal enabled flag for category groups enabled by SetEnabled().
+ ENABLED_FOR_RECORDING = 1 << 0,
+ // Category group enabled by SetEventCallbackEnabled().
+ ENABLED_FOR_EVENT_CALLBACK = 1 << 1,
+ };
- // Convert the given string to trace options. Defaults to RECORD_UNTIL_FULL if
- // the string does not provide valid options.
- static Options TraceOptionsFromString(const std::string& str);
+ static TraceLog* GetInstance();
// Get set of known category groups. This can change as new code paths are
// reached. The known category groups are inserted into |category_groups|.
void GetKnownCategoryGroups(std::vector<std::string>* category_groups);
- // Retrieves the current CategoryFilter.
- const CategoryFilter& GetCurrentCategoryFilter();
+ // Retrieves a copy (for thread-safety) of the current CategoryFilter.
+ CategoryFilter GetCurrentCategoryFilter();
Options trace_options() const {
return static_cast<Options>(subtle::NoBarrier_Load(&trace_options_));
}
- // Enables tracing. See CategoryFilter comments for details
- // on how to control what categories will be traced.
+ // Enables normal tracing (recording trace events in the trace buffer).
+ // 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);
- // Disable tracing for all categories.
+ // Disables normal tracing for all categories.
void SetDisabled();
- bool IsEnabled() { return !!enable_count_; }
+
+ bool IsEnabled() { return enabled_; }
// 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
@@ -350,6 +417,7 @@ class BASE_EXPORT TraceLog {
#if defined(OS_ANDROID)
void StartATrace();
void StopATrace();
+ void AddClockSyncMetadataEvent();
#endif
// Enabled state listeners give a callback when tracing is enabled or
@@ -370,21 +438,19 @@ class BASE_EXPORT TraceLog {
bool HasEnabledStateObserver(EnabledStateObserver* listener) const;
float GetBufferPercentFull() const;
-
- // Set the thread-safe notification callback. The callback can occur at any
- // time and from any thread. WARNING: It is possible for the previously set
- // callback to be called during OR AFTER a call to SetNotificationCallback.
- // Therefore, the target of the callback must either be a global function,
- // ref-counted object or a LazyInstance with Leaky traits (or equivalent).
- typedef base::Callback<void(int)> NotificationCallback;
- void SetNotificationCallback(const NotificationCallback& cb);
+ bool BufferIsFull() const;
// Not using base::Callback because of its limited by 7 parameters.
// Also, using primitive type allows directly passing callback from WebCore.
// WARNING: It is possible for the previously set callback to be called
- // after a call to SetEventCallback() that replaces or clears the callback.
+ // after a call to SetEventCallbackEnabled() that replaces or a call to
+ // SetEventCallbackDisabled() that disables the callback.
// This callback may be invoked on any thread.
- typedef void (*EventCallback)(char phase,
+ // For TRACE_EVENT_PHASE_COMPLETE events, the client will still receive pairs
+ // of TRACE_EVENT_PHASE_BEGIN and TRACE_EVENT_PHASE_END events to keep the
+ // interface simple.
+ typedef void (*EventCallback)(TimeTicks timestamp,
+ char phase,
const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
@@ -393,7 +459,11 @@ class BASE_EXPORT TraceLog {
const unsigned char arg_types[],
const unsigned long long arg_values[],
unsigned char flags);
- void SetEventCallback(EventCallback cb);
+
+ // Enable tracing for EventCallback.
+ void SetEventCallbackEnabled(const CategoryFilter& category_filter,
+ EventCallback cb);
+ void SetEventCallbackDisabled();
// Flush all collected events to the given output callback. The callback will
// be called one or more times either synchronously or asynchronously from
@@ -407,6 +477,7 @@ class BASE_EXPORT TraceLog {
typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&,
bool has_more_events)> OutputCallback;
void Flush(const OutputCallback& cb);
+ void FlushButLeaveBufferIntact(const OutputCallback& flush_output_callback);
// Called by TRACE_EVENT* macros, don't call this directly.
// The name parameter is a category group for example:
@@ -418,17 +489,18 @@ class BASE_EXPORT TraceLog {
// Called by TRACE_EVENT* macros, don't call this directly.
// If |copy| is set, |name|, |arg_name1| and |arg_name2| will be deep copied
// into the event; see "Memory scoping note" and TRACE_EVENT_COPY_XXX above.
- void AddTraceEvent(char phase,
- const unsigned char* category_group_enabled,
- const char* name,
- unsigned long long id,
- int num_args,
- const char** arg_names,
- const unsigned char* arg_types,
- const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
- unsigned char flags);
- void AddTraceEventWithThreadIdAndTimestamp(
+ TraceEventHandle AddTraceEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
+ unsigned char flags);
+ TraceEventHandle AddTraceEventWithThreadIdAndTimestamp(
char phase,
const unsigned char* category_group_enabled,
const char* name,
@@ -439,7 +511,7 @@ class BASE_EXPORT TraceLog {
const char** arg_names,
const unsigned char* arg_types,
const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values,
unsigned char flags);
static void AddTraceEventEtw(char phase,
const char* category_group,
@@ -450,12 +522,15 @@ class BASE_EXPORT TraceLog {
const void* id,
const std::string& extra);
- // For every matching event, a notification will be fired. NOTE: the
- // notification will fire for each matching event that has already occurred
- // since tracing was started (including before tracing if the process was
- // started with tracing turned on).
+ void UpdateTraceEventDuration(const unsigned char* category_group_enabled,
+ const char* name,
+ TraceEventHandle handle);
+
+ // For every matching event, the callback will be called.
+ typedef base::Callback<void()> WatchEventCallback;
void SetWatchEvent(const std::string& category_name,
- const std::string& event_name);
+ const std::string& event_name,
+ const WatchEventCallback& callback);
// Cancel the watch event. If tracing is enabled, this may race with the
// watch event notification firing.
void CancelWatchEvent();
@@ -464,16 +539,14 @@ class BASE_EXPORT TraceLog {
// Exposed for unittesting:
- void InstallWaitableEventForSamplingTesting(WaitableEvent* waitable_event);
+ void WaitSamplingEventForTesting();
// Allows deleting our singleton instance.
static void DeleteForTesting();
// Allow tests to inspect TraceEvents.
size_t GetEventsSize() const { return logged_events_->Size(); }
- const TraceEvent& GetEventAt(size_t index) const {
- return logged_events_->GetEventAt(index);
- }
+ TraceEvent* GetEventByHandle(TraceEventHandle handle);
void SetProcessID(int process_id);
@@ -501,95 +574,86 @@ class BASE_EXPORT TraceLog {
size_t GetObserverCountForTest() const;
+ // Call this method if the current thread may block the message loop to
+ // prevent the thread from using the thread-local buffer because the thread
+ // may not handle the flush request in time causing lost of unflushed events.
+ void SetCurrentThreadBlocksMessageLoop();
+
private:
+ FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+ TraceBufferRingBufferGetReturnChunk);
+ FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+ TraceBufferRingBufferHalfIteration);
+ FRIEND_TEST_ALL_PREFIXES(TraceEventTestFixture,
+ TraceBufferRingBufferFullIteration);
+
// This allows constructor and destructor to be private and usable only
// by the Singleton class.
friend struct DefaultSingletonTraits<TraceLog>;
- // Enable/disable each category group based on the current enable_count_
- // and category_filter_. Disable the category group if enabled_count_ is 0, or
- // if the category group contains a category that matches an included category
- // pattern, that category group will be enabled.
- // On Android, ATRACE_ENABLED flag will be applied if atrace is started.
+ // Enable/disable each category group based on the current enabled_,
+ // category_filter_, event_callback_ and event_callback_category_filter_.
+ // Enable the category group if enabled_ is true and 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);
- static void SetCategoryGroupEnabled(int category_index, bool enabled);
- static bool IsCategoryGroupEnabled(
- const unsigned char* category_group_enabled);
-
- // 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.
- enum CategoryGroupEnabledFlags {
- // Normal enabled flag for category groups enabled with Enable().
- CATEGORY_GROUP_ENABLED = 1 << 0,
- // On Android if ATrace is enabled, all categories will have this bit.
- // Not used on other platforms.
- ATRACE_ENABLED = 1 << 1
- };
-
- // Helper class for managing notification_thread_count_ and running
- // notification callbacks. This is very similar to a reader-writer lock, but
- // shares the lock with TraceLog and manages the notification flags.
- class NotificationHelper {
- public:
- inline explicit NotificationHelper(TraceLog* trace_log);
- inline ~NotificationHelper();
-
- // Called only while TraceLog::lock_ is held. This ORs the given
- // notification with any existing notifications.
- inline void AddNotificationWhileLocked(int notification);
-
- // Called only while TraceLog::lock_ is NOT held. If there are any pending
- // notifications from previous calls to AddNotificationWhileLocked, this
- // will call the NotificationCallback.
- inline void SendNotificationIfAny();
-
- private:
- TraceLog* trace_log_;
- NotificationCallback callback_copy_;
- int notification_;
- };
-
class ThreadLocalEventBuffer;
+ class OptionalAutoLock;
TraceLog();
~TraceLog();
const unsigned char* GetCategoryGroupEnabledInternal(const char* name);
- void AddMetadataEvents();
+ void AddMetadataEventsWhileLocked();
-#if defined(OS_ANDROID)
- void SendToATrace(char phase,
- const char* category_group,
- const char* name,
- unsigned long long id,
- int num_args,
- const char** arg_names,
- const unsigned char* arg_types,
- const unsigned long long* arg_values,
- scoped_ptr<ConvertableToTraceFormat> convertable_values[],
- unsigned char flags);
- static void ApplyATraceEnabledFlag(unsigned char* category_group_enabled);
-#endif
+ TraceBuffer* trace_buffer() const { return logged_events_.get(); }
+ TraceBuffer* CreateTraceBuffer();
+
+ std::string EventToConsoleMessage(unsigned char phase,
+ const TimeTicks& timestamp,
+ TraceEvent* trace_event);
- TraceBuffer* GetTraceBuffer();
+ TraceEvent* AddEventToThreadSharedChunkWhileLocked(TraceEventHandle* handle,
+ bool check_buffer_is_full);
+ void CheckIfBufferIsFullWhileLocked();
+ void SetDisabledWhileLocked();
- void AddEventToMainBufferWhileLocked(const TraceEvent& trace_event);
- void CheckIfBufferIsFullWhileLocked(NotificationHelper* notifier);
- // |flush_count| is used in the following callbacks to check if the callback
- // is called for the current flush.
- void FlushCurrentThread(int flush_count);
- void FinishFlush(int flush_count);
- void OnFlushTimeout(int flush_count);
+ TraceEvent* GetEventByHandleInternal(TraceEventHandle handle,
+ OptionalAutoLock* lock);
- // This lock protects TraceLog member accesses from arbitrary threads.
- Lock lock_;
+ // |generation| is used in the following callbacks to check if the callback
+ // is called for the flush of the current |logged_events_|.
+ void FlushCurrentThread(int generation);
+ void ConvertTraceEventsToTraceFormat(scoped_ptr<TraceBuffer> logged_events,
+ const TraceLog::OutputCallback& flush_output_callback);
+ void FinishFlush(int generation);
+ void OnFlushTimeout(int generation);
+
+ int generation() const {
+ return static_cast<int>(subtle::NoBarrier_Load(&generation_));
+ }
+ bool CheckGeneration(int generation) const {
+ return generation == this->generation();
+ }
+ void UseNextTraceBuffer();
+
+ TimeTicks OffsetNow() const {
+ return OffsetTimestamp(TimeTicks::NowFromSystemTraceTime());
+ }
+ TimeTicks OffsetTimestamp(const TimeTicks& timestamp) const {
+ return timestamp - time_offset_;
+ }
+
+ // This lock protects TraceLog member accesses (except for members protected
+ // by thread_info_lock_) from arbitrary threads.
+ mutable Lock lock_;
+ // This lock protects accesses to thread_names_, thread_event_start_times_
+ // and thread_colors_.
+ Lock thread_info_lock_;
int locked_line_;
- int enable_count_;
+ bool enabled_;
int num_traces_recorded_;
- subtle::AtomicWord /* bool */ buffer_is_full_;
- NotificationCallback notification_callback_;
scoped_ptr<TraceBuffer> logged_events_;
subtle::AtomicWord /* EventCallback */ event_callback_;
bool dispatching_to_observer_list_;
@@ -613,6 +677,7 @@ class BASE_EXPORT TraceLog {
TimeDelta time_offset_;
// Allow tests to wake up when certain events occur.
+ WatchEventCallback watch_event_callback_;
subtle::AtomicWord /* const unsigned char* */ watch_category_;
std::string watch_event_name_;
@@ -623,18 +688,26 @@ class BASE_EXPORT TraceLog {
PlatformThreadHandle sampling_thread_handle_;
CategoryFilter category_filter_;
+ CategoryFilter event_callback_category_filter_;
ThreadLocalPointer<ThreadLocalEventBuffer> thread_local_event_buffer_;
+ ThreadLocalBoolean thread_blocks_message_loop_;
+ ThreadLocalBoolean thread_is_in_trace_event_;
// Contains the message loops of threads that have had at least one event
// added into the local event buffer. Not using MessageLoopProxy because we
// need to know the life time of the message loops.
- base::hash_set<MessageLoop*> thread_message_loops_;
+ hash_set<MessageLoop*> thread_message_loops_;
+
+ // For events which can't be added into the thread local buffer, e.g. events
+ // from threads without a message loop.
+ scoped_ptr<TraceBufferChunk> thread_shared_chunk_;
+ size_t thread_shared_chunk_index_;
// Set when asynchronous Flush is in progress.
OutputCallback flush_output_callback_;
scoped_refptr<MessageLoopProxy> flush_message_loop_proxy_;
- int flush_count_;
+ subtle::AtomicWord generation_;
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
diff --git a/chromium/base/debug/trace_event_memory.cc b/chromium/base/debug/trace_event_memory.cc
index 42655b310e5..4b2b050df8e 100644
--- a/chromium/base/debug/trace_event_memory.cc
+++ b/chromium/base/debug/trace_event_memory.cc
@@ -31,7 +31,6 @@ class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat {
// Takes ownership of dump, which must be a JSON string, allocated with
// malloc() and NULL terminated.
explicit MemoryDumpHolder(char* dump) : dump_(dump) {}
- virtual ~MemoryDumpHolder() { free(dump_); }
// base::debug::ConvertableToTraceFormat overrides:
virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
@@ -39,6 +38,8 @@ class MemoryDumpHolder : public base::debug::ConvertableToTraceFormat {
}
private:
+ virtual ~MemoryDumpHolder() { free(dump_); }
+
char* dump_;
DISALLOW_COPY_AND_ASSIGN(MemoryDumpHolder);
@@ -216,13 +217,12 @@ void TraceMemoryController::DumpMemoryProfile() {
// MemoryDumpHolder takes ownership of this string. See GetHeapProfile() in
// tcmalloc for details.
char* dump = get_heap_profile_function_();
- scoped_ptr<MemoryDumpHolder> dump_holder(new MemoryDumpHolder(dump));
const int kSnapshotId = 1;
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("memory"),
"memory::Heap",
kSnapshotId,
- dump_holder.PassAs<base::debug::ConvertableToTraceFormat>());
+ scoped_refptr<ConvertableToTraceFormat>(new MemoryDumpHolder(dump)));
}
void TraceMemoryController::StopProfiling() {
@@ -246,11 +246,8 @@ bool TraceMemoryController::IsTimerRunningForTest() const {
// static
bool ScopedTraceMemory::enabled_ = false;
-ScopedTraceMemory::ScopedTraceMemory(const char* category, const char* name) {
- // Not enabled indicates that the trace system isn't running, so don't
- // record anything.
- if (!enabled_)
- return;
+void ScopedTraceMemory::Initialize(const char* category, const char* name) {
+ DCHECK(enabled_);
// Get our thread's copy of the stack.
TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
const size_t index = trace_memory_stack->scope_depth;
@@ -264,11 +261,8 @@ ScopedTraceMemory::ScopedTraceMemory(const char* category, const char* name) {
trace_memory_stack->scope_depth++;
}
-ScopedTraceMemory::~ScopedTraceMemory() {
- // Not enabled indicates that the trace system isn't running, so don't
- // record anything.
- if (!enabled_)
- return;
+void ScopedTraceMemory::Destroy() {
+ DCHECK(enabled_);
// Get our thread's copy of the stack.
TraceMemoryStack* trace_memory_stack = GetTraceMemoryStack();
// The tracing system can be turned on with ScopedTraceMemory objects
diff --git a/chromium/base/debug/trace_event_memory.h b/chromium/base/debug/trace_event_memory.h
index 5427154039a..df2e6638266 100644
--- a/chromium/base/debug/trace_event_memory.h
+++ b/chromium/base/debug/trace_event_memory.h
@@ -94,8 +94,16 @@ class BASE_EXPORT ScopedTraceMemory {
// Memory for |category| and |name| must be static, for example, literal
// strings in a TRACE_EVENT macro.
- ScopedTraceMemory(const char* category, const char* name);
- ~ScopedTraceMemory();
+ ScopedTraceMemory(const char* category, const char* name) {
+ if (!enabled_)
+ return;
+ Initialize(category, name);
+ }
+ ~ScopedTraceMemory() {
+ if (!enabled_)
+ return;
+ Destroy();
+ }
// Enables the storing of trace names on a per-thread stack.
static void set_enabled(bool enabled) { enabled_ = enabled; }
@@ -107,6 +115,9 @@ class BASE_EXPORT ScopedTraceMemory {
static ScopeData GetScopeDataForTest(int stack_index);
private:
+ void Initialize(const char* category, const char* name);
+ void Destroy();
+
static bool enabled_;
DISALLOW_COPY_AND_ASSIGN(ScopedTraceMemory);
};
diff --git a/chromium/base/debug/trace_event_system_stats_monitor.cc b/chromium/base/debug/trace_event_system_stats_monitor.cc
index b95e712b6af..a7128386d60 100644
--- a/chromium/base/debug/trace_event_system_stats_monitor.cc
+++ b/chromium/base/debug/trace_event_system_stats_monitor.cc
@@ -25,7 +25,6 @@ namespace {
class SystemStatsHolder : public base::debug::ConvertableToTraceFormat {
public:
SystemStatsHolder() { }
- virtual ~SystemStatsHolder() { }
// Fills system_metrics_ with profiled system memory and disk stats.
// Uses the previous stats to compute rates if this is not the first profile.
@@ -37,6 +36,8 @@ class SystemStatsHolder : public base::debug::ConvertableToTraceFormat {
}
private:
+ virtual ~SystemStatsHolder() { }
+
SystemMetrics system_stats_;
DISALLOW_COPY_AND_ASSIGN(SystemStatsHolder);
@@ -103,14 +104,14 @@ void TraceEventSystemStatsMonitor::StartProfiling() {
// If system tracing is enabled, dumps a profile to the tracing system.
void TraceEventSystemStatsMonitor::DumpSystemStats() {
- scoped_ptr<SystemStatsHolder> dump_holder(new SystemStatsHolder());
+ scoped_refptr<SystemStatsHolder> dump_holder = new SystemStatsHolder();
dump_holder->GetSystemProfilingStats();
TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(
TRACE_DISABLED_BY_DEFAULT("system_stats"),
"base::TraceEventSystemStatsMonitor::SystemStats",
this,
- dump_holder.PassAs<base::debug::ConvertableToTraceFormat>());
+ scoped_refptr<ConvertableToTraceFormat>(dump_holder));
}
void TraceEventSystemStatsMonitor::StopProfiling() {
diff --git a/chromium/base/debug/trace_event_unittest.cc b/chromium/base/debug/trace_event_unittest.cc
index e7883d02132..aef2c43b6fa 100644
--- a/chromium/base/debug/trace_event_unittest.cc
+++ b/chromium/base/debug/trace_event_unittest.cc
@@ -53,10 +53,8 @@ class TraceEventTestFixture : public testing::Test {
WaitableEvent* flush_complete_event,
const scoped_refptr<base::RefCountedString>& events_str,
bool has_more_events);
- void OnTraceNotification(int notification) {
- if (notification & TraceLog::EVENT_WATCH_NOTIFICATION)
- ++event_watch_notification_;
- notifications_received_ |= notification;
+ void OnWatchEventMatched() {
+ ++event_watch_notification_;
}
DictionaryValue* FindMatchingTraceEntry(const JsonKeyValue* key_values);
DictionaryValue* FindNamePhase(const char* name, const char* phase);
@@ -79,7 +77,6 @@ class TraceEventTestFixture : public testing::Test {
void BeginSpecificTrace(const std::string& filter) {
event_watch_notification_ = 0;
- notifications_received_ = 0;
TraceLog::GetInstance()->SetEnabled(CategoryFilter(filter),
TraceLog::RECORD_UNTIL_FULL);
}
@@ -90,28 +87,50 @@ class TraceEventTestFixture : public testing::Test {
flush_complete_event.Wait();
}
+ // Used when testing thread-local buffers which requires the thread initiating
+ // flush to have a message loop.
+ void EndTraceAndFlushInThreadWithMessageLoop() {
+ WaitableEvent flush_complete_event(false, false);
+ Thread flush_thread("flush");
+ flush_thread.Start();
+ flush_thread.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync,
+ base::Unretained(this),
+ &flush_complete_event));
+ flush_complete_event.Wait();
+ }
+
void EndTraceAndFlushAsync(WaitableEvent* flush_complete_event) {
- while (TraceLog::GetInstance()->IsEnabled())
- TraceLog::GetInstance()->SetDisabled();
+ TraceLog::GetInstance()->SetDisabled();
TraceLog::GetInstance()->Flush(
base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
base::Unretained(static_cast<TraceEventTestFixture*>(this)),
base::Unretained(flush_complete_event)));
}
+ void FlushMonitoring() {
+ WaitableEvent flush_complete_event(false, false);
+ FlushMonitoring(&flush_complete_event);
+ flush_complete_event.Wait();
+ }
+
+ void FlushMonitoring(WaitableEvent* flush_complete_event) {
+ TraceLog::GetInstance()->FlushButLeaveBufferIntact(
+ base::Bind(&TraceEventTestFixture::OnTraceDataCollected,
+ base::Unretained(static_cast<TraceEventTestFixture*>(this)),
+ base::Unretained(flush_complete_event)));
+ }
+
virtual void SetUp() OVERRIDE {
const char* name = PlatformThread::GetName();
old_thread_name_ = name ? strdup(name) : NULL;
- notifications_received_ = 0;
TraceLog::DeleteForTesting();
TraceLog* tracelog = TraceLog::GetInstance();
ASSERT_TRUE(tracelog);
ASSERT_FALSE(tracelog->IsEnabled());
- tracelog->SetNotificationCallback(
- base::Bind(&TraceEventTestFixture::OnTraceNotification,
- base::Unretained(this)));
trace_buffer_.SetOutputCallback(json_output_.GetCallback());
+ event_watch_notification_ = 0;
}
virtual void TearDown() OVERRIDE {
if (TraceLog::GetInstance())
@@ -128,7 +147,6 @@ class TraceEventTestFixture : public testing::Test {
base::debug::TraceResultBuffer trace_buffer_;
base::debug::TraceResultBuffer::SimpleOutput json_output_;
int event_watch_notification_;
- int notifications_received_;
private:
// We want our singleton torn down after each test.
@@ -333,6 +351,8 @@ std::vector<const DictionaryValue*> FindTraceEntries(
return hits;
}
+const char* kControlCharacters = "\001\002\003\n\r";
+
void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
{
TRACE_EVENT_BEGIN_ETW("TRACE_EVENT_BEGIN_ETW call", 0x1122, "extrastring1");
@@ -374,10 +394,10 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
"name1", "value1",
"name2", "value2");
- TRACE_EVENT_ASYNC_STEP0("all", "TRACE_EVENT_ASYNC_STEP0 call",
- 5, "step1");
- TRACE_EVENT_ASYNC_STEP1("all", "TRACE_EVENT_ASYNC_STEP1 call",
- 5, "step2", "name1", "value1");
+ TRACE_EVENT_ASYNC_STEP_INTO0("all", "TRACE_EVENT_ASYNC_STEP_INTO0 call",
+ kAsyncId, "step_begin1");
+ TRACE_EVENT_ASYNC_STEP_INTO1("all", "TRACE_EVENT_ASYNC_STEP_INTO1 call",
+ kAsyncId, "step_begin2", "name1", "value1");
TRACE_EVENT_ASYNC_END0("all", "TRACE_EVENT_ASYNC_END0 call", kAsyncId);
TRACE_EVENT_ASYNC_END1("all", "TRACE_EVENT_ASYNC_END1 call", kAsyncId,
@@ -412,6 +432,11 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0 call",
kAsyncId2, kThreadId, 34567);
+ TRACE_EVENT_ASYNC_STEP_PAST0("all", "TRACE_EVENT_ASYNC_STEP_PAST0 call",
+ kAsyncId2, "step_end1");
+ TRACE_EVENT_ASYNC_STEP_PAST1("all", "TRACE_EVENT_ASYNC_STEP_PAST1 call",
+ kAsyncId2, "step_end2", "name1", "value1");
+
TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0("all",
"TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call",
kAsyncId2, kThreadId, 45678);
@@ -424,6 +449,9 @@ void TraceWithAllMacroVariants(WaitableEvent* task_complete_event) {
TraceScopedTrackableObject<int> trackable("all", "tracked object 2",
0x2128506);
trackable.snapshot("world");
+
+ TRACE_EVENT1(kControlCharacters, kControlCharacters,
+ kControlCharacters, kControlCharacters);
} // Scope close causes TRACE_EVENT0 etc to send their END events.
if (task_complete_event)
@@ -453,15 +481,13 @@ void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
EXPECT_FIND_("TRACE_EVENT_INSTANT_ETW call");
EXPECT_FIND_("TRACE_EVENT0 call");
{
- std::string ph_begin;
+ std::string ph;
std::string ph_end;
EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call")));
- EXPECT_TRUE((item && item->GetString("ph", &ph_begin)));
- EXPECT_TRUE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call",
+ EXPECT_TRUE((item && item->GetString("ph", &ph)));
+ EXPECT_EQ("X", ph);
+ EXPECT_FALSE((item = FindTraceEntry(trace_parsed, "TRACE_EVENT0 call",
item)));
- EXPECT_TRUE((item && item->GetString("ph", &ph_end)));
- EXPECT_EQ("B", ph_begin);
- EXPECT_EQ("E", ph_end);
}
EXPECT_FIND_("TRACE_EVENT1 call");
EXPECT_SUB_FIND_("name1");
@@ -533,14 +559,14 @@ void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
EXPECT_SUB_FIND_("name2");
EXPECT_SUB_FIND_("value2");
- EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP0 call");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO0 call");
EXPECT_SUB_FIND_("id");
EXPECT_SUB_FIND_(kAsyncIdStr);
- EXPECT_SUB_FIND_("step1");
- EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP1 call");
+ EXPECT_SUB_FIND_("step_begin1");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_INTO1 call");
EXPECT_SUB_FIND_("id");
EXPECT_SUB_FIND_(kAsyncIdStr);
- EXPECT_SUB_FIND_("step2");
+ EXPECT_SUB_FIND_("step_begin2");
EXPECT_SUB_FIND_("name1");
EXPECT_SUB_FIND_("value1");
@@ -685,6 +711,19 @@ void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
EXPECT_EQ(kAsyncId2Str, id);
}
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST0 call");
+ {
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_(kAsyncId2Str);
+ EXPECT_SUB_FIND_("step_end1");
+ EXPECT_FIND_("TRACE_EVENT_ASYNC_STEP_PAST1 call");
+ EXPECT_SUB_FIND_("id");
+ EXPECT_SUB_FIND_(kAsyncId2Str);
+ EXPECT_SUB_FIND_("step_end2");
+ EXPECT_SUB_FIND_("name1");
+ EXPECT_SUB_FIND_("value1");
+ }
+
EXPECT_FIND_("TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0 call");
{
int val;
@@ -752,6 +791,9 @@ void ValidateAllTraceMacrosCreatedData(const ListValue& trace_parsed) {
EXPECT_TRUE(item && item->GetString("id", &id));
EXPECT_EQ("0x2128506", id);
}
+
+ EXPECT_FIND_(kControlCharacters);
+ EXPECT_SUB_FIND_(kControlCharacters);
}
void TraceManyInstantEvents(int thread_id, int num_events,
@@ -799,13 +841,6 @@ void ValidateInstantEventPresentOnEveryThread(const ListValue& trace_parsed,
}
}
-void TraceCallsWithCachedCategoryPointersPointers(const char* name_str) {
- TRACE_EVENT0("category name1", name_str);
- TRACE_EVENT_INSTANT0("category name2", name_str, TRACE_EVENT_SCOPE_THREAD);
- TRACE_EVENT_BEGIN0("category name3", name_str);
- TRACE_EVENT_END0("category name4", name_str);
-}
-
} // namespace
void HighResSleepForTraceTest(base::TimeDelta elapsed) {
@@ -872,7 +907,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnSecondEnable) {
TraceLog::GetInstance()->SetDisabled();
}
-TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnNestedDisable) {
+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);
@@ -883,7 +918,7 @@ TEST_F(TraceEventTestFixture, EnabledObserverDoesntFireOnNestedDisable) {
EXPECT_CALL(observer, OnTraceLogEnabled())
.Times(0);
EXPECT_CALL(observer, OnTraceLogDisabled())
- .Times(0);
+ .Times(1);
TraceLog::GetInstance()->SetDisabled();
testing::Mock::VerifyAndClear(&observer);
@@ -1146,21 +1181,17 @@ TEST_F(TraceEventTestFixture, Categories) {
TEST_F(TraceEventTestFixture, EventWatchNotification) {
// Basic one occurrence.
BeginTrace();
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::WatchEventCallback callback =
+ base::Bind(&TraceEventTestFixture::OnWatchEventMatched,
+ base::Unretained(this));
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
EXPECT_EQ(event_watch_notification_, 1);
- // Basic one occurrence before Set.
- BeginTrace();
- TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
- EndTraceAndFlush();
- EXPECT_EQ(event_watch_notification_, 1);
-
// Auto-reset after end trace.
BeginTrace();
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
EndTraceAndFlush();
BeginTrace();
TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
@@ -1170,7 +1201,7 @@ TEST_F(TraceEventTestFixture, EventWatchNotification) {
// Multiple occurrence.
BeginTrace();
int num_occurrences = 5;
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
for (int i = 0; i < num_occurrences; ++i)
TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
@@ -1178,21 +1209,21 @@ TEST_F(TraceEventTestFixture, EventWatchNotification) {
// Wrong category.
BeginTrace();
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
TRACE_EVENT_INSTANT0("wrong_cat", "event", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
EXPECT_EQ(event_watch_notification_, 0);
// Wrong name.
BeginTrace();
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
TRACE_EVENT_INSTANT0("cat", "wrong_event", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
EXPECT_EQ(event_watch_notification_, 0);
// Canceled.
BeginTrace();
- TraceLog::GetInstance()->SetWatchEvent("cat", "event");
+ TraceLog::GetInstance()->SetWatchEvent("cat", "event", callback);
TraceLog::GetInstance()->CancelWatchEvent();
TRACE_EVENT_INSTANT0("cat", "event", TRACE_EVENT_SCOPE_THREAD);
EndTraceAndFlush();
@@ -1205,10 +1236,11 @@ TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) {
unsigned long long id = 0xfeedbeeffeedbeefull;
TRACE_EVENT_ASYNC_BEGIN0( "cat", "name1", id);
- TRACE_EVENT_ASYNC_STEP0( "cat", "name1", id, "step1");
+ TRACE_EVENT_ASYNC_STEP_INTO0( "cat", "name1", id, "step1");
TRACE_EVENT_ASYNC_END0("cat", "name1", id);
TRACE_EVENT_BEGIN0( "cat", "name2");
TRACE_EVENT_ASYNC_BEGIN0( "cat", "name3", 0);
+ TRACE_EVENT_ASYNC_STEP_PAST0( "cat", "name3", 0, "step2");
EndTraceAndFlush();
@@ -1223,6 +1255,7 @@ TEST_F(TraceEventTestFixture, AsyncBeginEndEvents) {
EXPECT_TRUE(FindNamePhaseKeyValue("name1", "T", "id", id_str.c_str()));
EXPECT_TRUE(FindNamePhaseKeyValue("name1", "F", "id", id_str.c_str()));
EXPECT_TRUE(FindNamePhaseKeyValue("name3", "S", "id", "0x0"));
+ EXPECT_TRUE(FindNamePhaseKeyValue("name3", "p", "id", "0x0"));
// BEGIN events should not have id
EXPECT_FALSE(FindNamePhaseKeyValue("name2", "B", "id", "0"));
@@ -1271,48 +1304,62 @@ TEST_F(TraceEventTestFixture, StaticStringVsString) {
// Make sure old events are flushed:
EndTraceAndFlush();
EXPECT_EQ(0u, tracer->GetEventsSize());
+ const unsigned char* category_group_enabled =
+ TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED("cat");
{
BeginTrace();
// Test that string arguments are copied.
- TRACE_EVENT2("cat", "name1",
- "arg1", std::string("argval"), "arg2", std::string("argval"));
+ base::debug::TraceEventHandle handle1 =
+ trace_event_internal::AddTraceEvent(
+ TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1", 0, 0,
+ "arg1", std::string("argval"), "arg2", std::string("argval"));
// Test that static TRACE_STR_COPY string arguments are copied.
- TRACE_EVENT2("cat", "name2",
- "arg1", TRACE_STR_COPY("argval"),
- "arg2", TRACE_STR_COPY("argval"));
+ base::debug::TraceEventHandle handle2 =
+ trace_event_internal::AddTraceEvent(
+ TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", 0, 0,
+ "arg1", TRACE_STR_COPY("argval"),
+ "arg2", TRACE_STR_COPY("argval"));
size_t num_events = tracer->GetEventsSize();
EXPECT_GT(num_events, 1u);
- const TraceEvent& event1 = tracer->GetEventAt(num_events - 2);
- const TraceEvent& event2 = tracer->GetEventAt(num_events - 1);
- EXPECT_STREQ("name1", event1.name());
- EXPECT_STREQ("name2", event2.name());
- EXPECT_TRUE(event1.parameter_copy_storage() != NULL);
- EXPECT_TRUE(event2.parameter_copy_storage() != NULL);
- EXPECT_GT(event1.parameter_copy_storage()->size(), 0u);
- EXPECT_GT(event2.parameter_copy_storage()->size(), 0u);
+ const TraceEvent* event1 = tracer->GetEventByHandle(handle1);
+ const TraceEvent* event2 = tracer->GetEventByHandle(handle2);
+ ASSERT_TRUE(event1);
+ ASSERT_TRUE(event2);
+ EXPECT_STREQ("name1", event1->name());
+ EXPECT_STREQ("name2", event2->name());
+ EXPECT_TRUE(event1->parameter_copy_storage() != NULL);
+ EXPECT_TRUE(event2->parameter_copy_storage() != NULL);
+ EXPECT_GT(event1->parameter_copy_storage()->size(), 0u);
+ EXPECT_GT(event2->parameter_copy_storage()->size(), 0u);
EndTraceAndFlush();
}
{
BeginTrace();
// Test that static literal string arguments are not copied.
- TRACE_EVENT2("cat", "name1",
- "arg1", "argval", "arg2", "argval");
+ base::debug::TraceEventHandle handle1 =
+ trace_event_internal::AddTraceEvent(
+ TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name1", 0, 0,
+ "arg1", "argval", "arg2", "argval");
// Test that static TRACE_STR_COPY NULL string arguments are not copied.
const char* str1 = NULL;
const char* str2 = NULL;
- TRACE_EVENT2("cat", "name2",
- "arg1", TRACE_STR_COPY(str1),
- "arg2", TRACE_STR_COPY(str2));
+ base::debug::TraceEventHandle handle2 =
+ trace_event_internal::AddTraceEvent(
+ TRACE_EVENT_PHASE_INSTANT, category_group_enabled, "name2", 0, 0,
+ "arg1", TRACE_STR_COPY(str1),
+ "arg2", TRACE_STR_COPY(str2));
size_t num_events = tracer->GetEventsSize();
EXPECT_GT(num_events, 1u);
- const TraceEvent& event1 = tracer->GetEventAt(num_events - 2);
- const TraceEvent& event2 = tracer->GetEventAt(num_events - 1);
- EXPECT_STREQ("name1", event1.name());
- EXPECT_STREQ("name2", event2.name());
- EXPECT_TRUE(event1.parameter_copy_storage() == NULL);
- EXPECT_TRUE(event2.parameter_copy_storage() == NULL);
+ const TraceEvent* event1 = tracer->GetEventByHandle(handle1);
+ const TraceEvent* event2 = tracer->GetEventByHandle(handle2);
+ ASSERT_TRUE(event1);
+ ASSERT_TRUE(event2);
+ EXPECT_STREQ("name1", event1->name());
+ EXPECT_STREQ("name2", event2->name());
+ EXPECT_TRUE(event1->parameter_copy_storage() == NULL);
+ EXPECT_TRUE(event2->parameter_copy_storage() == NULL);
EndTraceAndFlush();
}
}
@@ -1362,14 +1409,7 @@ TEST_F(TraceEventTestFixture, DataCapturedManyThreads) {
delete task_complete_events[i];
}
- WaitableEvent flush_complete_event(false, false);
- Thread flush_thread("flush");
- flush_thread.Start();
- flush_thread.message_loop()->PostTask(FROM_HERE,
- base::Bind(&TraceEventTestFixture::EndTraceAndFlushAsync,
- base::Unretained(this),
- &flush_complete_event));
- flush_complete_event.Wait();
+ EndTraceAndFlushInThreadWithMessageLoop();
ValidateInstantEventPresentOnEveryThread(trace_parsed_,
num_threads, num_events);
@@ -1636,7 +1676,7 @@ TEST_F(TraceEventTestFixture, TraceEnableDisable) {
trace_log->SetEnabled(CategoryFilter(""), TraceLog::RECORD_UNTIL_FULL);
EXPECT_TRUE(trace_log->IsEnabled());
trace_log->SetDisabled();
- EXPECT_TRUE(trace_log->IsEnabled());
+ EXPECT_FALSE(trace_log->IsEnabled());
trace_log->SetDisabled();
EXPECT_FALSE(trace_log->IsEnabled());
}
@@ -1690,35 +1730,16 @@ TEST_F(TraceEventTestFixture, TraceCategoriesAfterNestedEnable) {
trace_log->SetDisabled();
}
-TEST_F(TraceEventTestFixture, TraceOptionsParsing) {
- EXPECT_EQ(TraceLog::RECORD_UNTIL_FULL,
- TraceLog::TraceOptionsFromString(std::string()));
-
- EXPECT_EQ(TraceLog::RECORD_UNTIL_FULL,
- TraceLog::TraceOptionsFromString("record-until-full"));
- EXPECT_EQ(TraceLog::RECORD_CONTINUOUSLY,
- TraceLog::TraceOptionsFromString("record-continuously"));
- EXPECT_EQ(TraceLog::RECORD_UNTIL_FULL | TraceLog::ENABLE_SAMPLING,
- TraceLog::TraceOptionsFromString("enable-sampling"));
- EXPECT_EQ(TraceLog::RECORD_CONTINUOUSLY | TraceLog::ENABLE_SAMPLING,
- TraceLog::TraceOptionsFromString(
- "record-continuously,enable-sampling"));
-}
-
TEST_F(TraceEventTestFixture, TraceSampling) {
- event_watch_notification_ = 0;
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("*"),
TraceLog::Options(TraceLog::RECORD_UNTIL_FULL |
TraceLog::ENABLE_SAMPLING));
- WaitableEvent* sampled = new WaitableEvent(false, false);
- TraceLog::GetInstance()->InstallWaitableEventForSamplingTesting(sampled);
-
TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "cc", "Stuff");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "cc", "Things");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
EndTraceAndFlush();
@@ -1728,53 +1749,95 @@ TEST_F(TraceEventTestFixture, TraceSampling) {
}
TEST_F(TraceEventTestFixture, TraceSamplingScope) {
- event_watch_notification_ = 0;
TraceLog::GetInstance()->SetEnabled(
CategoryFilter("*"),
TraceLog::Options(TraceLog::RECORD_UNTIL_FULL |
TraceLog::ENABLE_SAMPLING));
- WaitableEvent* sampled = new WaitableEvent(false, false);
- TraceLog::GetInstance()->InstallWaitableEventForSamplingTesting(sampled);
-
TRACE_EVENT_SCOPED_SAMPLING_STATE("AAA", "name");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
{
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
TRACE_EVENT_SCOPED_SAMPLING_STATE("BBB", "name");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "BBB");
}
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
{
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
TRACE_EVENT_SCOPED_SAMPLING_STATE("CCC", "name");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "CCC");
}
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
{
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "AAA");
TRACE_EVENT_SET_SAMPLING_STATE("DDD", "name");
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "DDD");
}
- sampled->Wait();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
EXPECT_STREQ(TRACE_EVENT_GET_SAMPLING_STATE(), "DDD");
EndTraceAndFlush();
}
+TEST_F(TraceEventTestFixture, TraceContinuousSampling) {
+ TraceLog::GetInstance()->SetEnabled(
+ CategoryFilter("*"),
+ TraceLog::Options(TraceLog::MONITOR_SAMPLING));
+
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "AAA");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "BBB");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+
+ FlushMonitoring();
+
+ // Make sure we can get the profiled data.
+ EXPECT_TRUE(FindNamePhase("AAA", "P"));
+ EXPECT_TRUE(FindNamePhase("BBB", "P"));
+
+ Clear();
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "CCC");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+ TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(1, "category", "DDD");
+ TraceLog::GetInstance()->WaitSamplingEventForTesting();
+
+ FlushMonitoring();
+
+ // Make sure the profiled data is accumulated.
+ EXPECT_TRUE(FindNamePhase("AAA", "P"));
+ EXPECT_TRUE(FindNamePhase("BBB", "P"));
+ EXPECT_TRUE(FindNamePhase("CCC", "P"));
+ EXPECT_TRUE(FindNamePhase("DDD", "P"));
+
+ Clear();
+
+ TraceLog::GetInstance()->SetDisabled();
+
+ // Make sure disabling the continuous sampling thread clears
+ // the profiled data.
+ EXPECT_FALSE(FindNamePhase("AAA", "P"));
+ EXPECT_FALSE(FindNamePhase("BBB", "P"));
+ EXPECT_FALSE(FindNamePhase("CCC", "P"));
+ EXPECT_FALSE(FindNamePhase("DDD", "P"));
+
+ Clear();
+}
+
class MyData : public base::debug::ConvertableToTraceFormat {
public:
MyData() {}
- virtual ~MyData() {}
virtual void AppendAsTraceFormat(std::string* out) const OVERRIDE {
out->append("{\"foo\":1}");
}
private:
+ virtual ~MyData() {}
DISALLOW_COPY_AND_ASSIGN(MyData);
};
@@ -1782,36 +1845,35 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) {
TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
TraceLog::RECORD_UNTIL_FULL);
- scoped_ptr<MyData> data(new MyData());
- scoped_ptr<MyData> data1(new MyData());
- scoped_ptr<MyData> data2(new MyData());
- TRACE_EVENT1("foo", "bar", "data",
- data.PassAs<base::debug::ConvertableToTraceFormat>());
+ scoped_refptr<ConvertableToTraceFormat> data(new MyData());
+ scoped_refptr<ConvertableToTraceFormat> data1(new MyData());
+ scoped_refptr<ConvertableToTraceFormat> data2(new MyData());
+ TRACE_EVENT1("foo", "bar", "data", data);
TRACE_EVENT2("foo", "baz",
- "data1", data1.PassAs<base::debug::ConvertableToTraceFormat>(),
- "data2", data2.PassAs<base::debug::ConvertableToTraceFormat>());
+ "data1", data1,
+ "data2", data2);
- scoped_ptr<MyData> convertData1(new MyData());
- scoped_ptr<MyData> convertData2(new MyData());
+ scoped_refptr<ConvertableToTraceFormat> convertData1(new MyData());
+ scoped_refptr<ConvertableToTraceFormat> convertData2(new MyData());
TRACE_EVENT2(
"foo",
"string_first",
"str",
"string value 1",
"convert",
- convertData1.PassAs<base::debug::ConvertableToTraceFormat>());
+ convertData1);
TRACE_EVENT2(
"foo",
"string_second",
"convert",
- convertData2.PassAs<base::debug::ConvertableToTraceFormat>(),
+ convertData2,
"str",
"string value 2");
EndTraceAndFlush();
// One arg version.
- DictionaryValue* dict = FindNamePhase("bar", "B");
+ DictionaryValue* dict = FindNamePhase("bar", "X");
ASSERT_TRUE(dict);
const DictionaryValue* args_dict = NULL;
@@ -1828,7 +1890,7 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) {
EXPECT_EQ(1, foo_val);
// Two arg version.
- dict = FindNamePhase("baz", "B");
+ dict = FindNamePhase("baz", "X");
ASSERT_TRUE(dict);
args_dict = NULL;
@@ -1846,7 +1908,7 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) {
ASSERT_TRUE(value->GetAsDictionary(&convertable_dict));
// Convertable with other types.
- dict = FindNamePhase("string_first", "B");
+ dict = FindNamePhase("string_first", "X");
ASSERT_TRUE(dict);
args_dict = NULL;
@@ -1865,7 +1927,7 @@ TEST_F(TraceEventTestFixture, ConvertableTypes) {
EXPECT_TRUE(convertable_dict->GetInteger("foo", &foo_val));
EXPECT_EQ(1, foo_val);
- dict = FindNamePhase("string_second", "B");
+ dict = FindNamePhase("string_second", "X");
ASSERT_TRUE(dict);
args_dict = NULL;
@@ -1892,19 +1954,49 @@ class TraceEventCallbackTest : public TraceEventTestFixture {
s_instance = this;
}
virtual void TearDown() OVERRIDE {
- while (TraceLog::GetInstance()->IsEnabled())
- TraceLog::GetInstance()->SetDisabled();
+ TraceLog::GetInstance()->SetDisabled();
ASSERT_TRUE(!!s_instance);
s_instance = NULL;
TraceEventTestFixture::TearDown();
}
protected:
- std::vector<std::string> collected_events_;
+ // For TraceEventCallbackAndRecordingX tests.
+ void VerifyCallbackAndRecordedEvents(size_t expected_callback_count,
+ size_t expected_recorded_count) {
+ // Callback events.
+ EXPECT_EQ(expected_callback_count, collected_events_names_.size());
+ for (size_t i = 0; i < collected_events_names_.size(); ++i) {
+ EXPECT_EQ("callback", collected_events_categories_[i]);
+ EXPECT_EQ("yes", collected_events_names_[i]);
+ }
+
+ // Recorded events.
+ EXPECT_EQ(expected_recorded_count, trace_parsed_.GetSize());
+ EXPECT_TRUE(FindTraceEntry(trace_parsed_, "recording"));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, "callback"));
+ EXPECT_TRUE(FindTraceEntry(trace_parsed_, "yes"));
+ EXPECT_FALSE(FindTraceEntry(trace_parsed_, "no"));
+ }
+
+ void VerifyCollectedEvent(size_t i,
+ unsigned phase,
+ const std::string& category,
+ const std::string& name) {
+ EXPECT_EQ(phase, collected_events_phases_[i]);
+ EXPECT_EQ(category, collected_events_categories_[i]);
+ EXPECT_EQ(name, collected_events_names_[i]);
+ }
+
+ std::vector<std::string> collected_events_categories_;
+ std::vector<std::string> collected_events_names_;
+ std::vector<unsigned char> collected_events_phases_;
+ std::vector<TimeTicks> collected_events_timestamps_;
static TraceEventCallbackTest* s_instance;
- static void Callback(char phase,
- const unsigned char* category_enabled,
+ static void Callback(TimeTicks timestamp,
+ char phase,
+ const unsigned char* category_group_enabled,
const char* name,
unsigned long long id,
int num_args,
@@ -1912,7 +2004,11 @@ class TraceEventCallbackTest : public TraceEventTestFixture {
const unsigned char arg_types[],
const unsigned long long arg_values[],
unsigned char flags) {
- s_instance->collected_events_.push_back(name);
+ s_instance->collected_events_phases_.push_back(phase);
+ s_instance->collected_events_categories_.push_back(
+ TraceLog::GetCategoryGroupName(category_group_enabled));
+ s_instance->collected_events_names_.push_back(name);
+ s_instance->collected_events_timestamps_.push_back(timestamp);
}
};
@@ -1920,18 +2016,32 @@ TraceEventCallbackTest* TraceEventCallbackTest::s_instance;
TEST_F(TraceEventCallbackTest, TraceEventCallback) {
TRACE_EVENT_INSTANT0("all", "before enable", TRACE_EVENT_SCOPE_THREAD);
- TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
- TraceLog::RECORD_UNTIL_FULL);
- TRACE_EVENT_INSTANT0("all", "before callback set", TRACE_EVENT_SCOPE_THREAD);
- TraceLog::GetInstance()->SetEventCallback(Callback);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(
+ CategoryFilter("*"), Callback);
TRACE_EVENT_INSTANT0("all", "event1", TRACE_EVENT_SCOPE_GLOBAL);
TRACE_EVENT_INSTANT0("all", "event2", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEventCallback(NULL);
+ {
+ TRACE_EVENT0("all", "duration");
+ TRACE_EVENT_INSTANT0("all", "event3", TRACE_EVENT_SCOPE_GLOBAL);
+ }
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
TRACE_EVENT_INSTANT0("all", "after callback removed",
TRACE_EVENT_SCOPE_GLOBAL);
- ASSERT_EQ(2u, collected_events_.size());
- EXPECT_EQ("event1", collected_events_[0]);
- EXPECT_EQ("event2", collected_events_[1]);
+ ASSERT_EQ(5u, collected_events_names_.size());
+ EXPECT_EQ("event1", collected_events_names_[0]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[0]);
+ EXPECT_EQ("event2", collected_events_names_[1]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[1]);
+ EXPECT_EQ("duration", collected_events_names_[2]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, collected_events_phases_[2]);
+ EXPECT_EQ("event3", collected_events_names_[3]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[3]);
+ EXPECT_EQ("duration", collected_events_names_[4]);
+ EXPECT_EQ(TRACE_EVENT_PHASE_END, collected_events_phases_[4]);
+ for (size_t i = 1; i < collected_events_timestamps_.size(); i++) {
+ EXPECT_LE(collected_events_timestamps_[i - 1],
+ collected_events_timestamps_[i]);
+ }
}
TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) {
@@ -1939,15 +2049,236 @@ TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) {
TraceLog::RECORD_UNTIL_FULL);
do {
TRACE_EVENT_INSTANT0("all", "badger badger", TRACE_EVENT_SCOPE_GLOBAL);
- } while ((notifications_received_ & TraceLog::TRACE_BUFFER_FULL) == 0);
- TraceLog::GetInstance()->SetEventCallback(Callback);
+ } while (!TraceLog::GetInstance()->BufferIsFull());
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("*"),
+ Callback);
TRACE_EVENT_INSTANT0("all", "a snake", TRACE_EVENT_SCOPE_GLOBAL);
- TraceLog::GetInstance()->SetEventCallback(NULL);
- ASSERT_EQ(1u, collected_events_.size());
- EXPECT_EQ("a snake", collected_events_[0]);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ ASSERT_EQ(1u, collected_events_names_.size());
+ EXPECT_EQ("a snake", collected_events_names_[0]);
+}
+
+// 1: Enable callback, enable recording, disable callback, disable recording.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
+ 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);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ VerifyCallbackAndRecordedEvents(2, 2);
+}
+
+// 2: Enable callback, enable recording, disable recording, disable callback.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) {
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
+ 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);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ VerifyCallbackAndRecordedEvents(3, 1);
+}
+
+// 3: Enable recording, enable callback, disable callback, disable recording.
+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);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ VerifyCallbackAndRecordedEvents(1, 3);
+}
+
+// 4: Enable recording, enable callback, disable recording, disable callback.
+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);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"),
+ Callback);
+ TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ EndTraceAndFlush();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL);
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+ TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL);
+
+ VerifyCallbackAndRecordedEvents(2, 2);
}
-// TODO(dsinclair): Continuous Tracing unit test.
+TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) {
+ TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("*"),
+ Callback);
+ {
+ TRACE_EVENT0("callback", "duration1");
+ TraceLog::GetInstance()->SetEnabled(
+ CategoryFilter("*"), TraceLog::RECORD_UNTIL_FULL);
+ TRACE_EVENT0("callback", "duration2");
+ EndTraceAndFlush();
+ TRACE_EVENT0("callback", "duration3");
+ }
+ TraceLog::GetInstance()->SetEventCallbackDisabled();
+
+ ASSERT_EQ(6u, collected_events_names_.size());
+ VerifyCollectedEvent(0, TRACE_EVENT_PHASE_BEGIN, "callback", "duration1");
+ VerifyCollectedEvent(1, TRACE_EVENT_PHASE_BEGIN, "callback", "duration2");
+ VerifyCollectedEvent(2, TRACE_EVENT_PHASE_BEGIN, "callback", "duration3");
+ VerifyCollectedEvent(3, TRACE_EVENT_PHASE_END, "callback", "duration3");
+ VerifyCollectedEvent(4, TRACE_EVENT_PHASE_END, "callback", "duration2");
+ VerifyCollectedEvent(5, TRACE_EVENT_PHASE_END, "callback", "duration1");
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferGetReturnChunk) {
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ TraceLog::RECORD_CONTINUOUSLY);
+ TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+ size_t capacity = buffer->Capacity();
+ size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+ uint32 last_seq = 0;
+ size_t chunk_index;
+ EXPECT_EQ(0u, buffer->Size());
+
+ scoped_ptr<TraceBufferChunk*[]> chunks(new TraceBufferChunk*[num_chunks]);
+ for (size_t i = 0; i < num_chunks; ++i) {
+ chunks[i] = buffer->GetChunk(&chunk_index).release();
+ EXPECT_TRUE(chunks[i]);
+ EXPECT_EQ(i, chunk_index);
+ EXPECT_GT(chunks[i]->seq(), last_seq);
+ EXPECT_EQ((i + 1) * TraceBufferChunk::kTraceBufferChunkSize,
+ buffer->Size());
+ last_seq = chunks[i]->seq();
+ }
+
+ // Ring buffer is never full.
+ EXPECT_FALSE(buffer->IsFull());
+
+ // Return all chunks in original order.
+ for (size_t i = 0; i < num_chunks; ++i)
+ buffer->ReturnChunk(i, scoped_ptr<TraceBufferChunk>(chunks[i]));
+
+ // Should recycle the chunks in the returned order.
+ for (size_t i = 0; i < num_chunks; ++i) {
+ chunks[i] = buffer->GetChunk(&chunk_index).release();
+ EXPECT_TRUE(chunks[i]);
+ EXPECT_EQ(i, chunk_index);
+ EXPECT_GT(chunks[i]->seq(), last_seq);
+ last_seq = chunks[i]->seq();
+ }
+
+ // Return all chunks in reverse order.
+ for (size_t i = 0; i < num_chunks; ++i) {
+ buffer->ReturnChunk(
+ num_chunks - i - 1,
+ scoped_ptr<TraceBufferChunk>(chunks[num_chunks - i - 1]));
+ }
+
+ // Should recycle the chunks in the returned order.
+ for (size_t i = 0; i < num_chunks; ++i) {
+ chunks[i] = buffer->GetChunk(&chunk_index).release();
+ EXPECT_TRUE(chunks[i]);
+ EXPECT_EQ(num_chunks - i - 1, chunk_index);
+ EXPECT_GT(chunks[i]->seq(), last_seq);
+ last_seq = chunks[i]->seq();
+ }
+
+ for (size_t i = 0; i < num_chunks; ++i)
+ buffer->ReturnChunk(i, scoped_ptr<TraceBufferChunk>(chunks[i]));
+
+ TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferHalfIteration) {
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ TraceLog::RECORD_CONTINUOUSLY);
+ TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+ size_t capacity = buffer->Capacity();
+ size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+ size_t chunk_index;
+ EXPECT_EQ(0u, buffer->Size());
+ EXPECT_FALSE(buffer->NextChunk());
+
+ size_t half_chunks = num_chunks / 2;
+ scoped_ptr<TraceBufferChunk*[]> chunks(new TraceBufferChunk*[half_chunks]);
+
+ for (size_t i = 0; i < half_chunks; ++i) {
+ chunks[i] = buffer->GetChunk(&chunk_index).release();
+ EXPECT_TRUE(chunks[i]);
+ EXPECT_EQ(i, chunk_index);
+ }
+ for (size_t i = 0; i < half_chunks; ++i)
+ buffer->ReturnChunk(i, scoped_ptr<TraceBufferChunk>(chunks[i]));
+
+ for (size_t i = 0; i < half_chunks; ++i)
+ EXPECT_EQ(chunks[i], buffer->NextChunk());
+ EXPECT_FALSE(buffer->NextChunk());
+ TraceLog::GetInstance()->SetDisabled();
+}
+
+TEST_F(TraceEventTestFixture, TraceBufferRingBufferFullIteration) {
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ TraceLog::RECORD_CONTINUOUSLY);
+ TraceBuffer* buffer = TraceLog::GetInstance()->trace_buffer();
+ size_t capacity = buffer->Capacity();
+ size_t num_chunks = capacity / TraceBufferChunk::kTraceBufferChunkSize;
+ size_t chunk_index;
+ EXPECT_EQ(0u, buffer->Size());
+ EXPECT_FALSE(buffer->NextChunk());
+
+ scoped_ptr<TraceBufferChunk*[]> chunks(new TraceBufferChunk*[num_chunks]);
+
+ for (size_t i = 0; i < num_chunks; ++i) {
+ chunks[i] = buffer->GetChunk(&chunk_index).release();
+ EXPECT_TRUE(chunks[i]);
+ EXPECT_EQ(i, chunk_index);
+ }
+ for (size_t i = 0; i < num_chunks; ++i)
+ buffer->ReturnChunk(i, scoped_ptr<TraceBufferChunk>(chunks[i]));
+
+ for (size_t i = 0; i < num_chunks; ++i)
+ EXPECT_TRUE(chunks[i] == buffer->NextChunk());
+ EXPECT_FALSE(buffer->NextChunk());
+ TraceLog::GetInstance()->SetDisabled();
+}
// Test the category filter.
TEST_F(TraceEventTestFixture, CategoryFilter) {
@@ -2048,5 +2379,208 @@ TEST_F(TraceEventTestFixture, CategoryFilter) {
"good_category"));
}
+void BlockUntilStopped(WaitableEvent* task_start_event,
+ WaitableEvent* task_stop_event) {
+ task_start_event->Signal();
+ task_stop_event->Wait();
+}
+
+TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopBeforeTracing) {
+ BeginTrace();
+
+ Thread thread("1");
+ WaitableEvent task_complete_event(false, false);
+ thread.Start();
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&TraceLog::SetCurrentThreadBlocksMessageLoop,
+ Unretained(TraceLog::GetInstance())));
+
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+ task_complete_event.Wait();
+
+ WaitableEvent task_start_event(false, false);
+ WaitableEvent task_stop_event(false, false);
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+ task_start_event.Wait();
+
+ EndTraceAndFlush();
+ ValidateAllTraceMacrosCreatedData(trace_parsed_);
+
+ task_stop_event.Signal();
+ thread.Stop();
+}
+
+void SetBlockingFlagAndBlockUntilStopped(WaitableEvent* task_start_event,
+ WaitableEvent* task_stop_event) {
+ TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop();
+ BlockUntilStopped(task_start_event, task_stop_event);
+}
+
+TEST_F(TraceEventTestFixture, SetCurrentThreadBlocksMessageLoopAfterTracing) {
+ BeginTrace();
+
+ Thread thread("1");
+ WaitableEvent task_complete_event(false, false);
+ thread.Start();
+
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+ task_complete_event.Wait();
+
+ WaitableEvent task_start_event(false, false);
+ WaitableEvent task_stop_event(false, false);
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&SetBlockingFlagAndBlockUntilStopped,
+ &task_start_event, &task_stop_event));
+ task_start_event.Wait();
+
+ EndTraceAndFlush();
+ ValidateAllTraceMacrosCreatedData(trace_parsed_);
+
+ task_stop_event.Signal();
+ thread.Stop();
+}
+
+TEST_F(TraceEventTestFixture, ThreadOnceBlocking) {
+ BeginTrace();
+
+ Thread thread("1");
+ WaitableEvent task_complete_event(false, false);
+ thread.Start();
+
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+ task_complete_event.Wait();
+ task_complete_event.Reset();
+
+ WaitableEvent task_start_event(false, false);
+ WaitableEvent task_stop_event(false, false);
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+ task_start_event.Wait();
+
+ // The thread will timeout in this flush.
+ EndTraceAndFlushInThreadWithMessageLoop();
+ Clear();
+
+ // Let the thread's message loop continue to spin.
+ task_stop_event.Signal();
+
+ // The following sequence ensures that the FlushCurrentThread task has been
+ // executed in the thread before continuing.
+ task_start_event.Reset();
+ task_stop_event.Reset();
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&BlockUntilStopped, &task_start_event, &task_stop_event));
+ task_start_event.Wait();
+ task_stop_event.Signal();
+ Clear();
+
+ // TraceLog should discover the generation mismatch and recover the thread
+ // local buffer for the thread without any error.
+ BeginTrace();
+ thread.message_loop()->PostTask(
+ FROM_HERE, Bind(&TraceWithAllMacroVariants, &task_complete_event));
+ task_complete_event.Wait();
+ task_complete_event.Reset();
+ EndTraceAndFlushInThreadWithMessageLoop();
+ ValidateAllTraceMacrosCreatedData(trace_parsed_);
+}
+
+std::string* g_log_buffer = NULL;
+bool MockLogMessageHandler(int, const char*, int, size_t,
+ const std::string& str) {
+ if (!g_log_buffer)
+ g_log_buffer = new std::string();
+ g_log_buffer->append(str);
+ return false;
+}
+
+TEST_F(TraceEventTestFixture, EchoToConsole) {
+ logging::LogMessageHandlerFunction old_log_message_handler =
+ logging::GetLogMessageHandler();
+ logging::SetLogMessageHandler(MockLogMessageHandler);
+
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ TraceLog::ECHO_TO_CONSOLE);
+ TRACE_EVENT_BEGIN0("a", "begin_end");
+ {
+ TRACE_EVENT0("b", "duration");
+ TRACE_EVENT0("b1", "duration1");
+ }
+ TRACE_EVENT_INSTANT0("c", "instant", TRACE_EVENT_SCOPE_GLOBAL);
+ TRACE_EVENT_END0("a", "begin_end");
+
+ EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a]\x1b"));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b]\x1b"));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1]\x1b"));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("| | duration1[b1] ("));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("| duration[b] ("));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("| instant[c]\x1b"));
+ EXPECT_NE(std::string::npos, g_log_buffer->find("begin_end[a] ("));
+
+ EndTraceAndFlush();
+ delete g_log_buffer;
+ logging::SetLogMessageHandler(old_log_message_handler);
+ g_log_buffer = NULL;
+}
+
+bool LogMessageHandlerWithTraceEvent(int, const char*, int, size_t,
+ const std::string&) {
+ TRACE_EVENT0("log", "trace_event");
+ return false;
+}
+
+TEST_F(TraceEventTestFixture, EchoToConsoleTraceEventRecursion) {
+ logging::LogMessageHandlerFunction old_log_message_handler =
+ logging::GetLogMessageHandler();
+ logging::SetLogMessageHandler(LogMessageHandlerWithTraceEvent);
+
+ TraceLog::GetInstance()->SetEnabled(CategoryFilter("*"),
+ TraceLog::ECHO_TO_CONSOLE);
+ {
+ // This should not cause deadlock or infinite recursion.
+ TRACE_EVENT0("b", "duration");
+ }
+
+ EndTraceAndFlush();
+ logging::SetLogMessageHandler(old_log_message_handler);
+}
+
+TEST_F(TraceEventTestFixture, TimeOffset) {
+ BeginTrace();
+ // Let TraceLog timer start from 0.
+ TimeDelta time_offset = TimeTicks::NowFromSystemTraceTime() - TimeTicks();
+ TraceLog::GetInstance()->SetTimeOffset(time_offset);
+
+ {
+ TRACE_EVENT0("all", "duration1");
+ TRACE_EVENT0("all", "duration2");
+ }
+ TRACE_EVENT_BEGIN_WITH_ID_TID_AND_TIMESTAMP0(
+ "all", "with_timestamp", 0, 0,
+ TimeTicks::NowFromSystemTraceTime().ToInternalValue());
+ TRACE_EVENT_END_WITH_ID_TID_AND_TIMESTAMP0(
+ "all", "with_timestamp", 0, 0,
+ TimeTicks::NowFromSystemTraceTime().ToInternalValue());
+
+ EndTraceAndFlush();
+
+ double end_time = static_cast<double>(
+ (TimeTicks::NowFromSystemTraceTime() - time_offset).ToInternalValue());
+ double last_timestamp = 0;
+ for (size_t i = 0; i < trace_parsed_.GetSize(); ++i) {
+ const DictionaryValue* item;
+ EXPECT_TRUE(trace_parsed_.GetDictionary(i, &item));
+ double timestamp;
+ EXPECT_TRUE(item->GetDouble("ts", &timestamp));
+ EXPECT_GE(timestamp, last_timestamp);
+ EXPECT_LE(timestamp, end_time);
+ last_timestamp = timestamp;
+ }
+}
+
} // namespace debug
} // namespace base
diff --git a/chromium/base/debug/trace_event_win.cc b/chromium/base/debug/trace_event_win.cc
index d5a21f49268..686ade344c8 100644
--- a/chromium/base/debug/trace_event_win.cc
+++ b/chromium/base/debug/trace_event_win.cc
@@ -78,14 +78,17 @@ void TraceEventETWProvider::TraceEvent(const char* name,
event.SetField(1, sizeof(id), &id);
event.SetField(2, extra_len + 1, extra);
- // See whether we're to capture a backtrace.
+ // These variables are declared here so that they are not out of scope when
+ // the event is logged.
+ DWORD depth;
void* backtrace[32];
+
+ // See whether we're to capture a backtrace.
if (enable_flags() & CAPTURE_STACK_TRACE) {
- DWORD hash = 0;
- DWORD depth = CaptureStackBackTrace(0,
- arraysize(backtrace),
- backtrace,
- &hash);
+ depth = CaptureStackBackTrace(0,
+ arraysize(backtrace),
+ backtrace,
+ NULL);
event.SetField(3, sizeof(depth), &depth);
event.SetField(4, sizeof(backtrace[0]) * depth, backtrace);
}
diff --git a/chromium/base/debug/trace_event_win_unittest.cc b/chromium/base/debug/trace_event_win_unittest.cc
index 531c5f7d867..befd0cb405f 100644
--- a/chromium/base/debug/trace_event_win_unittest.cc
+++ b/chromium/base/debug/trace_event_win_unittest.cc
@@ -115,7 +115,7 @@ class TraceEventWinTest: public testing::Test {
}
// Create the log file.
- ASSERT_TRUE(file_util::CreateTemporaryFile(&log_file_));
+ ASSERT_TRUE(base::CreateTemporaryFile(&log_file_));
// Create a private log session on the file.
EtwTraceProperties prop;
diff --git a/chromium/base/event_recorder_win.cc b/chromium/base/event_recorder_win.cc
index 11bf0f0cb19..39c4220897e 100644
--- a/chromium/base/event_recorder_win.cc
+++ b/chromium/base/event_recorder_win.cc
@@ -49,7 +49,7 @@ bool EventRecorder::StartRecording(const FilePath& filename) {
// Open the recording file.
DCHECK(!file_);
- file_ = file_util::OpenFile(filename, "wb+");
+ file_ = OpenFile(filename, "wb+");
if (!file_) {
DLOG(ERROR) << "EventRecorder could not open log file";
return false;
@@ -63,7 +63,7 @@ bool EventRecorder::StartRecording(const FilePath& filename) {
GetModuleHandle(NULL), 0);
if (!journal_hook_) {
DLOG(ERROR) << "EventRecorder Record Hook failed";
- file_util::CloseFile(file_);
+ CloseFile(file_);
return false;
}
@@ -84,7 +84,7 @@ void EventRecorder::StopRecording() {
::timeEndPeriod(1);
DCHECK(file_ != NULL);
- file_util::CloseFile(file_);
+ CloseFile(file_);
file_ = NULL;
journal_hook_ = NULL;
@@ -100,7 +100,7 @@ bool EventRecorder::StartPlayback(const FilePath& filename) {
// Open the recording file.
DCHECK(!file_);
- file_ = file_util::OpenFile(filename, "rb");
+ file_ = OpenFile(filename, "rb");
if (!file_) {
DLOG(ERROR) << "EventRecorder Playback could not open log file";
return false;
@@ -108,7 +108,7 @@ bool EventRecorder::StartPlayback(const FilePath& filename) {
// Read the first event from the record.
if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
DLOG(ERROR) << "EventRecorder Playback has no records!";
- file_util::CloseFile(file_);
+ CloseFile(file_);
return false;
}
@@ -150,7 +150,7 @@ void EventRecorder::StopPlayback() {
}
DCHECK(file_ != NULL);
- file_util::CloseFile(file_);
+ CloseFile(file_);
file_ = NULL;
::timeEndPeriod(1);
diff --git a/chromium/base/file_util.cc b/chromium/base/file_util.cc
index e9806763855..1575a079d6b 100644
--- a/chromium/base/file_util.cc
+++ b/chromium/base/file_util.cc
@@ -23,8 +23,6 @@ namespace base {
namespace {
-const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.');
-
// The maximum number of 'uniquified' files we will try to create.
// This is used when the filename we're trying to download is already in use,
// so we create a new unique filename by appending " (nnn)" before the
@@ -34,8 +32,6 @@ static const int kMaxUniqueFiles = 100;
} // namespace
-bool g_bug108724_debug = false;
-
int64 ComputeDirectorySize(const FilePath& root_path) {
int64 running_size = 0;
FileEnumerator file_iter(root_path, true, FileEnumerator::FILES);
@@ -133,7 +129,7 @@ bool TextContentsEqual(const FilePath& filename1, const FilePath& filename2) {
bool ReadFileToString(const FilePath& path, std::string* contents) {
if (path.ReferencesParent())
return false;
- FILE* file = file_util::OpenFile(path, "rb");
+ FILE* file = OpenFile(path, "rb");
if (!file) {
return false;
}
@@ -144,22 +140,11 @@ bool ReadFileToString(const FilePath& path, std::string* contents) {
if (contents)
contents->append(buf, len);
}
- file_util::CloseFile(file);
+ CloseFile(file);
return true;
}
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::FileEnumerator;
-using base::FilePath;
-using base::kExtensionSeparator;
-using base::kMaxUniqueFiles;
-
bool IsDirectoryEmpty(const FilePath& dir_path) {
FileEnumerator files(dir_path, false,
FileEnumerator::FILES | FileEnumerator::DIRECTORIES);
@@ -176,12 +161,12 @@ FILE* CreateAndOpenTemporaryFile(FilePath* path) {
return CreateAndOpenTemporaryFileInDir(directory, path);
}
-bool CreateDirectory(const base::FilePath& full_path) {
+bool CreateDirectory(const FilePath& full_path) {
return CreateDirectoryAndGetError(full_path, NULL);
}
bool GetFileSize(const FilePath& file_path, int64* file_size) {
- base::PlatformFileInfo info;
+ PlatformFileInfo info;
if (!GetFileInfo(file_path, &info))
return false;
*file_size = info.size;
@@ -189,32 +174,26 @@ bool GetFileSize(const FilePath& file_path, int64* file_size) {
}
bool TouchFile(const FilePath& path,
- const base::Time& last_accessed,
- const base::Time& last_modified) {
- int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE_ATTRIBUTES;
+ const Time& last_accessed,
+ const Time& last_modified) {
+ int flags = PLATFORM_FILE_OPEN | PLATFORM_FILE_WRITE_ATTRIBUTES;
#if defined(OS_WIN)
// On Windows, FILE_FLAG_BACKUP_SEMANTICS is needed to open a directory.
if (DirectoryExists(path))
- flags |= base::PLATFORM_FILE_BACKUP_SEMANTICS;
+ flags |= PLATFORM_FILE_BACKUP_SEMANTICS;
#endif // OS_WIN
- const base::PlatformFile file =
- base::CreatePlatformFile(path, flags, NULL, NULL);
- if (file != base::kInvalidPlatformFileValue) {
- bool result = base::TouchPlatformFile(file, last_accessed, last_modified);
- base::ClosePlatformFile(file);
+ const PlatformFile file = CreatePlatformFile(path, flags, NULL, NULL);
+ if (file != kInvalidPlatformFileValue) {
+ bool result = TouchPlatformFile(file, last_accessed, last_modified);
+ ClosePlatformFile(file);
return result;
}
return false;
}
-bool SetLastModifiedTime(const FilePath& path,
- const base::Time& last_modified) {
- return TouchFile(path, last_modified, last_modified);
-}
-
bool CloseFile(FILE* file) {
if (file == NULL)
return true;
@@ -239,6 +218,15 @@ 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) {
diff --git a/chromium/base/file_util.h b/chromium/base/file_util.h
index b4150f7368a..3f892e3ad14 100644
--- a/chromium/base/file_util.h
+++ b/chromium/base/file_util.h
@@ -40,8 +40,6 @@ namespace base {
class Time;
-extern bool g_bug108724_debug;
-
//-----------------------------------------------------------------------------
// Functions that involve filesystem access or modification:
@@ -139,13 +137,8 @@ BASE_EXPORT bool TextContentsEqual(const FilePath& filename1,
// Useful for unit tests.
BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents);
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
#if defined(OS_POSIX)
+
// Read exactly |bytes| bytes from file descriptor |fd|, storing the result
// in |buffer|. This function is protected against EINTR and partial reads.
// Returns true iff |bytes| bytes have been successfully read from |fd|.
@@ -153,13 +146,12 @@ BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes);
// Creates a symbolic link at |symlink| pointing to |target|. Returns
// false on failure.
-BASE_EXPORT bool CreateSymbolicLink(const base::FilePath& target,
- const base::FilePath& symlink);
+BASE_EXPORT bool CreateSymbolicLink(const FilePath& target,
+ const FilePath& symlink);
// Reads the given |symlink| and returns where it points to in |target|.
// Returns false upon failure.
-BASE_EXPORT bool ReadSymbolicLink(const base::FilePath& symlink,
- base::FilePath* target);
+BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target);
// Bits ans masks of the file permission.
enum FilePermissionBits {
@@ -182,80 +174,95 @@ enum FilePermissionBits {
// Reads the permission of the given |path|, storing the file permission
// bits in |mode|. If |path| is symbolic link, |mode| is the permission of
// a file which the symlink points to.
-BASE_EXPORT bool GetPosixFilePermissions(const base::FilePath& path,
- int* mode);
+BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode);
// Sets the permission of the given |path|. If |path| is symbolic link, sets
// the permission of a file which the symlink points to.
-BASE_EXPORT bool SetPosixFilePermissions(const base::FilePath& path,
- int mode);
-#endif // defined(OS_POSIX)
+BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode);
+
+#endif // OS_POSIX
-// Return true if the given directory is empty
-BASE_EXPORT bool IsDirectoryEmpty(const base::FilePath& dir_path);
+// Returns true if the given directory is empty
+BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path);
// Get the temporary directory provided by the system.
-// WARNING: DON'T USE THIS. If you want to create a temporary file, use one of
-// the functions below.
-BASE_EXPORT bool GetTempDir(base::FilePath* path);
-// Get a temporary directory for shared memory files.
+//
+// WARNING: In general, you should use CreateTemporaryFile variants below
+// instead of this function. Those variants will ensure that the proper
+// permissions are set so that other users on the system can't edit them while
+// 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(base::FilePath* path, bool executable);
+BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path);
+#if defined(OS_POSIX)
// Get the home directory. This is more complicated than just getenv("HOME")
// as it knows to fall back on getpwent() etc.
-BASE_EXPORT base::FilePath GetHomeDir();
+//
+// 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.
+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
// be empty and all handles closed after this function returns.
-BASE_EXPORT bool CreateTemporaryFile(base::FilePath* path);
+BASE_EXPORT bool CreateTemporaryFile(FilePath* path);
// Same as CreateTemporaryFile but the file is created in |dir|.
-BASE_EXPORT bool CreateTemporaryFileInDir(const base::FilePath& dir,
- base::FilePath* temp_file);
+BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir,
+ FilePath* temp_file);
// Create and open a temporary file. File is opened for read/write.
// The full path is placed in |path|.
// Returns a handle to the opened file or NULL if an error occurred.
-BASE_EXPORT FILE* CreateAndOpenTemporaryFile(base::FilePath* path);
+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(base::FilePath* path,
+BASE_EXPORT FILE* CreateAndOpenTemporaryShmemFile(FilePath* path,
bool executable);
+
// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|.
-BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const base::FilePath& dir,
- base::FilePath* path);
+BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir,
+ FilePath* path);
// Create a new directory. If prefix is provided, the new directory name is in
// the format of prefixyyyy.
// NOTE: prefix is ignored in the POSIX implementation.
// If success, return true and output the full path of the directory created.
-BASE_EXPORT bool CreateNewTempDirectory(
- const base::FilePath::StringType& prefix,
- base::FilePath* new_temp_path);
+BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix,
+ FilePath* new_temp_path);
// Create a directory within another directory.
// Extra characters will be appended to |prefix| to ensure that the
// new directory does not have the same name as an existing directory.
-BASE_EXPORT bool CreateTemporaryDirInDir(
- const base::FilePath& base_dir,
- const base::FilePath::StringType& prefix,
- base::FilePath* new_dir);
+BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir,
+ const FilePath::StringType& prefix,
+ FilePath* new_dir);
// Creates a directory, as well as creating any parent directories, if they
// don't exist. Returns 'true' on successful creation, or if the directory
// already exists. The directory is only readable by the current user.
// 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 base::FilePath& full_path,
- base::PlatformFileError* error);
+BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path,
+ PlatformFileError* error);
// Backward-compatible convenience method for the above.
-BASE_EXPORT bool CreateDirectory(const base::FilePath& full_path);
+BASE_EXPORT bool CreateDirectory(const FilePath& full_path);
// Returns the file size. Returns true on success.
-BASE_EXPORT bool GetFileSize(const base::FilePath& file_path, int64* file_size);
+BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64* file_size);
// Sets |real_path| to |path| with symbolic links and junctions expanded.
// On windows, make sure the path starts with a lettered drive.
@@ -263,48 +270,37 @@ BASE_EXPORT bool GetFileSize(const base::FilePath& file_path, int64* file_size);
// a directory or to a nonexistent path. On windows, this function will
// fail if |path| is a junction or symlink that points to an empty file,
// or if |real_path| would be longer than MAX_PATH characters.
-BASE_EXPORT bool NormalizeFilePath(const base::FilePath& path,
- base::FilePath* real_path);
+BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path);
#if defined(OS_WIN)
// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."),
// return in |drive_letter_path| the equivalent path that starts with
// a drive letter ("C:\..."). Return false if no such path exists.
-BASE_EXPORT bool DevicePathToDriveLetterPath(const base::FilePath& device_path,
- base::FilePath* drive_letter_path);
+BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path,
+ FilePath* drive_letter_path);
// Given an existing file in |path|, set |real_path| to the path
// in native NT format, of the form "\Device\HarddiskVolumeXX\..".
// Returns false if the path can not be found. Empty files cannot
// be resolved with this function.
-BASE_EXPORT bool NormalizeToNativeFilePath(const base::FilePath& path,
- base::FilePath* nt_path);
+BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path,
+ FilePath* nt_path);
#endif
// This function will return if the given file is a symlink or not.
-BASE_EXPORT bool IsLink(const base::FilePath& file_path);
+BASE_EXPORT bool IsLink(const FilePath& file_path);
// Returns information about the given file path.
-BASE_EXPORT bool GetFileInfo(const base::FilePath& file_path,
- base::PlatformFileInfo* info);
+BASE_EXPORT bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* info);
// Sets the time of the last access and the time of the last modification.
-BASE_EXPORT bool TouchFile(const base::FilePath& path,
- const base::Time& last_accessed,
- const base::Time& last_modified);
-
-// Set the time of the last modification. Useful for unit tests.
-BASE_EXPORT bool SetLastModifiedTime(const base::FilePath& path,
- const base::Time& last_modified);
-
-#if defined(OS_POSIX)
-// Store inode number of |path| in |inode|. Return true on success.
-BASE_EXPORT bool GetInode(const base::FilePath& path, ino_t* inode);
-#endif
+BASE_EXPORT bool TouchFile(const FilePath& path,
+ const Time& last_accessed,
+ const Time& last_modified);
// Wrapper for fopen-like calls. Returns non-NULL FILE* on success.
-BASE_EXPORT FILE* OpenFile(const base::FilePath& filename, const char* mode);
+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);
@@ -317,6 +313,12 @@ BASE_EXPORT bool TruncateFile(FILE* file);
// 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 {
+
// 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,
@@ -401,7 +403,7 @@ class ScopedFDClose {
public:
inline void operator()(int* x) const {
if (x && *x >= 0) {
- if (HANDLE_EINTR(close(*x)) < 0)
+ if (IGNORE_EINTR(close(*x)) < 0)
DPLOG(ERROR) << "close";
}
}
diff --git a/chromium/base/file_util_android.cc b/chromium/base/file_util_android.cc
index 6ac9def8c1d..def4d7cab96 100644
--- a/chromium/base/file_util_android.cc
+++ b/chromium/base/file_util_android.cc
@@ -7,10 +7,10 @@
#include "base/files/file_path.h"
#include "base/path_service.h"
-namespace file_util {
+namespace base {
-bool GetShmemTempDir(base::FilePath* path, bool executable) {
+bool GetShmemTempDir(bool executable, base::FilePath* path) {
return PathService::Get(base::DIR_CACHE, path);
}
-} // namespace file_util
+} // namespace base
diff --git a/chromium/base/file_util_mac.mm b/chromium/base/file_util_mac.mm
index 9d9ac3d99b4..f8a6b6301f5 100644
--- a/chromium/base/file_util_mac.mm
+++ b/chromium/base/file_util_mac.mm
@@ -23,9 +23,6 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
}
} // namespace internal
-} // namepsace base
-
-namespace file_util {
bool GetTempDir(base::FilePath* path) {
NSString* tmp = NSTemporaryDirectory();
@@ -35,8 +32,8 @@ bool GetTempDir(base::FilePath* path) {
return true;
}
-bool GetShmemTempDir(base::FilePath* path, bool executable) {
+bool GetShmemTempDir(bool executable, base::FilePath* path) {
return GetTempDir(path);
}
-} // namespace
+} // namespace base
diff --git a/chromium/base/file_util_posix.cc b/chromium/base/file_util_posix.cc
index 762700ae42c..ddcd5ddb912 100644
--- a/chromium/base/file_util_posix.cc
+++ b/chromium/base/file_util_posix.cc
@@ -24,8 +24,8 @@
#if defined(OS_MACOSX)
#include <AvailabilityMacros.h>
#include "base/mac/foundation_util.h"
-#elif !defined(OS_ANDROID)
-#include <glib.h>
+#elif !defined(OS_CHROMEOS) && defined(USE_GLIB)
+#include <glib.h> // for g_get_home_dir()
#endif
#include <fstream>
@@ -43,10 +43,12 @@
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
#include "base/os_compat_android.h"
#endif
@@ -54,10 +56,6 @@
#include <grp.h>
#endif
-#if defined(OS_CHROMEOS)
-#include "base/chromeos/chromeos_version.h"
-#endif
-
namespace base {
namespace {
@@ -82,6 +80,12 @@ 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
// Helper for NormalizeFilePath(), defined below.
@@ -146,6 +150,47 @@ std::string TempFileName() {
#endif
}
+// Creates and opens a temporary file in |directory|, returning the
+// file descriptor. |path| is set to the temporary file path.
+// This function does NOT unlink() the file.
+int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
+ ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp().
+ *path = directory.Append(base::TempFileName());
+ const std::string& tmpdir_string = path->value();
+ // this should be OK since mkstemp just replaces characters in place
+ char* buffer = const_cast<char*>(tmpdir_string.c_str());
+
+ return HANDLE_EINTR(mkstemp(buffer));
+}
+
+#if defined(OS_LINUX)
+// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
+// This depends on the mount options used for /dev/shm, which vary among
+// different Linux distributions and possibly local configuration. It also
+// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
+// but its kernel allows mprotect with PROT_EXEC anyway.
+bool DetermineDevShmExecutable() {
+ bool result = false;
+ FilePath path;
+ int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path);
+ if (fd >= 0) {
+ file_util::ScopedFD shm_fd_closer(&fd);
+ 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);
+ if (mapping != MAP_FAILED) {
+ if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
+ result = true;
+ munmap(mapping, pagesize);
+ }
+ }
+ return result;
+}
+#endif // defined(OS_LINUX)
+
} // namespace
FilePath MakeAbsoluteFilePath(const FilePath& input) {
@@ -311,6 +356,11 @@ bool CopyDirectory(const FilePath& from_path,
bool PathExists(const FilePath& path) {
ThreadRestrictions::AssertIOAllowed();
+#if defined(OS_ANDROID)
+ if (path.IsContentUri()) {
+ return ContentUriExists(path);
+ }
+#endif
return access(path.value().c_str(), F_OK) == 0;
}
@@ -327,22 +377,6 @@ bool DirectoryExists(const FilePath& path) {
return false;
}
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::stat_wrapper_t;
-using base::CallStat;
-using base::CallLstat;
-using base::DirectoryExists;
-using base::FileEnumerator;
-using base::FilePath;
-using base::MakeAbsoluteFilePath;
-using base::RealPath;
-using base::VerifySpecificPathControlledByUser;
-
bool ReadFromFD(int fd, char* buffer, size_t bytes) {
size_t total_read = 0;
while (total_read < bytes) {
@@ -363,8 +397,7 @@ bool CreateSymbolicLink(const FilePath& target_path,
symlink_path.value().c_str()) != -1;
}
-bool ReadSymbolicLink(const FilePath& symlink_path,
- FilePath* target_path) {
+bool ReadSymbolicLink(const FilePath& symlink_path, FilePath* target_path) {
DCHECK(!symlink_path.empty());
DCHECK(target_path);
char buf[PATH_MAX];
@@ -380,7 +413,7 @@ bool ReadSymbolicLink(const FilePath& symlink_path,
}
bool GetPosixFilePermissions(const FilePath& path, int* mode) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
DCHECK(mode);
stat_wrapper_t file_info;
@@ -395,7 +428,7 @@ bool GetPosixFilePermissions(const FilePath& path, int* mode) {
bool SetPosixFilePermissions(const FilePath& path,
int mode) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
DCHECK((mode & ~FILE_PERMISSION_MASK) == 0);
// Calls stat() so that we can preserve the higher bits like S_ISGID.
@@ -413,34 +446,88 @@ bool SetPosixFilePermissions(const FilePath& path,
return true;
}
-// Creates and opens a temporary file in |directory|, returning the
-// file descriptor. |path| is set to the temporary file path.
-// This function does NOT unlink() the file.
-int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to mkstemp().
- *path = directory.Append(base::TempFileName());
- const std::string& tmpdir_string = path->value();
- // this should be OK since mkstemp just replaces characters in place
- char* buffer = const_cast<char*>(tmpdir_string.c_str());
+#if !defined(OS_MACOSX)
+// This is implemented in file_util_mac.mm for Mac.
+bool GetTempDir(FilePath* path) {
+ const char* tmp = getenv("TMPDIR");
+ if (tmp) {
+ *path = FilePath(tmp);
+ } else {
+#if defined(OS_ANDROID)
+ return PathService::Get(base::DIR_CACHE, path);
+#else
+ *path = FilePath("/tmp");
+#endif
+ }
+ return true;
+}
+#endif // !defined(OS_MACOSX)
- return HANDLE_EINTR(mkstemp(buffer));
+#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)
+FilePath GetHomeDir() {
+#if defined(OS_CHROMEOS)
+ if (SysInfo::IsRunningOnChromeOS())
+ return FilePath("/home/chronos/user");
+#endif
+
+ const char* home_dir = getenv("HOME");
+ if (home_dir && home_dir[0])
+ return FilePath(home_dir);
+
+#if defined(OS_ANDROID)
+ DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
+#elif defined(USE_GLIB) && !defined(OS_CHROMEOS)
+ // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
+ ThreadRestrictions::AssertIOAllowed();
+
+ home_dir = g_get_home_dir();
+ if (home_dir && home_dir[0])
+ return FilePath(home_dir);
+#endif
+
+ FilePath rv;
+ if (GetTempDir(&rv))
+ return rv;
+
+ // Last resort.
+ return FilePath("/tmp");
}
+#endif // !defined(OS_MACOSX)
bool CreateTemporaryFile(FilePath* path) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
+ ThreadRestrictions::AssertIOAllowed(); // For call to close().
FilePath directory;
if (!GetTempDir(&directory))
return false;
int fd = CreateAndOpenFdForTemporaryFile(directory, path);
if (fd < 0)
return false;
- ignore_result(HANDLE_EINTR(close(fd)));
+ close(fd);
return true;
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
FilePath directory;
- if (!GetShmemTempDir(&directory, executable))
+ if (!GetShmemTempDir(executable, &directory))
return NULL;
return CreateAndOpenTemporaryFileInDir(directory, path);
@@ -453,20 +540,20 @@ FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
FILE* file = fdopen(fd, "a+");
if (!file)
- ignore_result(HANDLE_EINTR(close(fd)));
+ close(fd);
return file;
}
bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to close().
+ ThreadRestrictions::AssertIOAllowed(); // For call to close().
int fd = CreateAndOpenFdForTemporaryFile(dir, temp_file);
- return ((fd >= 0) && !HANDLE_EINTR(close(fd)));
+ return ((fd >= 0) && !IGNORE_EINTR(close(fd)));
}
static bool CreateTemporaryDirInDirImpl(const FilePath& base_dir,
const FilePath::StringType& name_tmpl,
FilePath* new_dir) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp().
+ ThreadRestrictions::AssertIOAllowed(); // For call to mkdtemp().
DCHECK(name_tmpl.find("XXXXXX") != FilePath::StringType::npos)
<< "Directory name template must contain \"XXXXXX\".";
@@ -498,13 +585,12 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix,
if (!GetTempDir(&tmpdir))
return false;
- return CreateTemporaryDirInDirImpl(tmpdir, base::TempFileName(),
- new_temp_path);
+ return CreateTemporaryDirInDirImpl(tmpdir, TempFileName(), new_temp_path);
}
bool CreateDirectoryAndGetError(const FilePath& full_path,
- base::PlatformFileError* error) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
+ PlatformFileError* error) {
+ ThreadRestrictions::AssertIOAllowed(); // For call to mkdir().
std::vector<FilePath> subpaths;
// Collect a list of all parent directories.
@@ -530,29 +616,27 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
int saved_errno = errno;
if (!DirectoryExists(*i)) {
if (error)
- *error = base::ErrnoToPlatformFileError(saved_errno);
+ *error = ErrnoToPlatformFileError(saved_errno);
return false;
}
}
return true;
}
-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();
+bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
+ FilePath real_path_result;
+ if (!RealPath(path, &real_path_result))
+ return false;
+
+ // To be consistant with windows, fail if |real_path_result| is a
+ // directory.
+ stat_wrapper_t file_info;
+ if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
+ S_ISDIR(file_info.st_mode))
+ return false;
+
+ *normalized_path = real_path_result;
+ return true;
}
// TODO(rkc): Refactor GetFileInfo and FileEnumerator to handle symlinks
@@ -570,45 +654,43 @@ bool IsLink(const FilePath& file_path) {
return false;
}
-bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
+bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) {
stat_wrapper_t file_info;
- if (CallStat(file_path.value().c_str(), &file_info) != 0)
- return false;
+#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)
+ return false;
+ } else {
+#endif // defined(OS_ANDROID)
+ if (CallStat(file_path.value().c_str(), &file_info) != 0)
+ return false;
+#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 = base::Time::FromTimeSpec(file_info.st_mtimespec);
- results->last_accessed = base::Time::FromTimeSpec(file_info.st_atimespec);
- results->creation_time = base::Time::FromTimeSpec(file_info.st_ctimespec);
+ 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 = base::Time::FromTimeT(file_info.st_mtime);
- results->last_accessed = base::Time::FromTimeT(file_info.st_atime);
- results->creation_time = base::Time::FromTimeT(file_info.st_ctime);
+ 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 = base::Time::FromTimeSpec(file_info.st_mtim);
- results->last_accessed = base::Time::FromTimeSpec(file_info.st_atim);
- results->creation_time = base::Time::FromTimeSpec(file_info.st_ctim);
+ 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
return true;
}
-bool GetInode(const FilePath& path, ino_t* inode) {
- base::ThreadRestrictions::AssertIOAllowed(); // For call to stat().
- struct stat buffer;
- int result = stat(path.value().c_str(), &buffer);
- if (result < 0)
- return false;
-
- *inode = buffer.st_ino;
- return true;
-}
-
-FILE* OpenFile(const std::string& filename, const char* mode) {
- return OpenFile(FilePath(filename), mode);
-}
-
FILE* OpenFile(const FilePath& filename, const char* mode) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
FILE* result = NULL;
do {
result = fopen(filename.value().c_str(), mode);
@@ -617,17 +699,55 @@ FILE* OpenFile(const FilePath& filename, const char* mode) {
}
int ReadFile(const FilePath& filename, char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
int fd = HANDLE_EINTR(open(filename.value().c_str(), O_RDONLY));
if (fd < 0)
return -1;
ssize_t bytes_read = HANDLE_EINTR(read(fd, data, size));
- if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ if (int ret = IGNORE_EINTR(close(fd)) < 0)
return ret;
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));
@@ -635,7 +755,7 @@ int WriteFile(const FilePath& filename, const char* data, int size) {
return -1;
int bytes_written = WriteFileDescriptor(fd, data, size);
- if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ if (int ret = IGNORE_EINTR(close(fd)) < 0)
return ret;
return bytes_written;
}
@@ -662,7 +782,7 @@ int AppendToFile(const FilePath& filename, const char* data, int size) {
return -1;
int bytes_written = WriteFileDescriptor(fd, data, size);
- if (int ret = HANDLE_EINTR(close(fd)) < 0)
+ if (int ret = IGNORE_EINTR(close(fd)) < 0)
return ret;
return bytes_written;
}
@@ -688,117 +808,6 @@ bool SetCurrentDirectory(const FilePath& path) {
return !ret;
}
-bool NormalizeFilePath(const FilePath& path, FilePath* normalized_path) {
- FilePath real_path_result;
- if (!RealPath(path, &real_path_result))
- return false;
-
- // To be consistant with windows, fail if |real_path_result| is a
- // directory.
- stat_wrapper_t file_info;
- if (CallStat(real_path_result.value().c_str(), &file_info) != 0 ||
- S_ISDIR(file_info.st_mode))
- return false;
-
- *normalized_path = real_path_result;
- return true;
-}
-
-#if !defined(OS_MACOSX)
-bool GetTempDir(FilePath* path) {
- const char* tmp = getenv("TMPDIR");
- if (tmp)
- *path = FilePath(tmp);
- else
-#if defined(OS_ANDROID)
- return PathService::Get(base::DIR_CACHE, path);
-#else
- *path = FilePath("/tmp");
-#endif
- return true;
-}
-
-#if !defined(OS_ANDROID)
-
-#if defined(OS_LINUX)
-// Determine if /dev/shm files can be mapped and then mprotect'd PROT_EXEC.
-// This depends on the mount options used for /dev/shm, which vary among
-// different Linux distributions and possibly local configuration. It also
-// depends on details of kernel--ChromeOS uses the noexec option for /dev/shm
-// but its kernel allows mprotect with PROT_EXEC anyway.
-
-namespace {
-
-bool DetermineDevShmExecutable() {
- bool result = false;
- FilePath path;
- int fd = CreateAndOpenFdForTemporaryFile(FilePath("/dev/shm"), &path);
- if (fd >= 0) {
- ScopedFD shm_fd_closer(&fd);
- 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);
- if (mapping != MAP_FAILED) {
- if (mprotect(mapping, pagesize, PROT_READ | PROT_EXEC) == 0)
- result = true;
- munmap(mapping, pagesize);
- }
- }
- return result;
-}
-
-}; // namespace
-#endif // defined(OS_LINUX)
-
-bool GetShmemTempDir(FilePath* path, bool executable) {
-#if defined(OS_LINUX)
- bool use_dev_shm = true;
- if (executable) {
- static const bool s_dev_shm_executable = DetermineDevShmExecutable();
- use_dev_shm = s_dev_shm_executable;
- }
- if (use_dev_shm) {
- *path = FilePath("/dev/shm");
- return true;
- }
-#endif
- return GetTempDir(path);
-}
-#endif // !defined(OS_ANDROID)
-
-FilePath GetHomeDir() {
-#if defined(OS_CHROMEOS)
- if (base::chromeos::IsRunningOnChromeOS())
- return FilePath("/home/chronos/user");
-#endif
-
- const char* home_dir = getenv("HOME");
- if (home_dir && home_dir[0])
- return FilePath(home_dir);
-
-#if defined(OS_ANDROID)
- DLOG(WARNING) << "OS_ANDROID: Home directory lookup not yet implemented.";
-#else
- // g_get_home_dir calls getpwent, which can fall through to LDAP calls.
- base::ThreadRestrictions::AssertIOAllowed();
-
- home_dir = g_get_home_dir();
- if (home_dir && home_dir[0])
- return FilePath(home_dir);
-#endif
-
- FilePath rv;
- if (file_util::GetTempDir(&rv))
- return rv;
-
- // Last resort.
- return FilePath("/tmp");
-}
-#endif // !defined(OS_MACOSX)
-
bool VerifyPathControlledByUser(const FilePath& base,
const FilePath& path,
uid_t owner_uid,
@@ -914,7 +923,7 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
int outfile = HANDLE_EINTR(creat(to_path.value().c_str(), 0666));
if (outfile < 0) {
- ignore_result(HANDLE_EINTR(close(infile)));
+ close(infile);
return false;
}
@@ -945,9 +954,9 @@ bool CopyFileUnsafe(const FilePath& from_path, const FilePath& to_path) {
} while (bytes_written_per_read < bytes_read);
}
- if (HANDLE_EINTR(close(infile)) < 0)
+ if (IGNORE_EINTR(close(infile)) < 0)
result = false;
- if (HANDLE_EINTR(close(outfile)) < 0)
+ if (IGNORE_EINTR(close(outfile)) < 0)
result = false;
return result;
diff --git a/chromium/base/file_util_unittest.cc b/chromium/base/file_util_unittest.cc
index 787b6d50ba7..b65e171719f 100644
--- a/chromium/base/file_util_unittest.cc
+++ b/chromium/base/file_util_unittest.cc
@@ -33,14 +33,14 @@
#include "base/win/windows_version.h"
#endif
+#if defined(OS_ANDROID)
+#include "base/android/content_uri_utils.h"
+#endif
+
// This macro helps avoid wrapped lines in the test structs.
#define FPL(x) FILE_PATH_LITERAL(x)
-using base::DirectoryExists;
-using base::FileEnumerator;
-using base::FilePath;
-using base::PathIsWritable;
-using base::TextContentsEqual;
+namespace base {
namespace {
@@ -142,7 +142,7 @@ class ReparsePoint {
bool IsValid() { return created_; }
private:
- base::win::ScopedHandle dir_;
+ win::ScopedHandle dir_;
bool created_;
DISALLOW_COPY_AND_ASSIGN(ReparsePoint);
};
@@ -160,10 +160,10 @@ void ChangePosixFilePermissions(const FilePath& path,
<< "Can't set and clear the same bits.";
int mode = 0;
- ASSERT_TRUE(file_util::GetPosixFilePermissions(path, &mode));
+ ASSERT_TRUE(GetPosixFilePermissions(path, &mode));
mode |= mode_bits_to_set;
mode &= ~mode_bits_to_clear;
- ASSERT_TRUE(file_util::SetPosixFilePermissions(path, mode));
+ ASSERT_TRUE(SetPosixFilePermissions(path, mode));
}
#endif // defined(OS_POSIX)
@@ -181,7 +181,7 @@ class FileUtilTest : public PlatformTest {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
}
- base::ScopedTempDir temp_dir_;
+ ScopedTempDir temp_dir_;
};
// Collects all the results from the given file enumerator, and provides an
@@ -244,133 +244,31 @@ uint64 FileTimeAsUint64(const FILETIME& ft) {
}
#endif
-const struct append_case {
- const wchar_t* path;
- const wchar_t* ending;
- const wchar_t* result;
-} append_cases[] = {
-#if defined(OS_WIN)
- {L"c:\\colon\\backslash", L"path", L"c:\\colon\\backslash\\path"},
- {L"c:\\colon\\backslash\\", L"path", L"c:\\colon\\backslash\\path"},
- {L"c:\\colon\\backslash\\\\", L"path", L"c:\\colon\\backslash\\\\path"},
- {L"c:\\colon\\backslash\\", L"", L"c:\\colon\\backslash\\"},
- {L"c:\\colon\\backslash", L"", L"c:\\colon\\backslash\\"},
- {L"", L"path", L"\\path"},
- {L"", L"", L"\\"},
-#elif defined(OS_POSIX)
- {L"/foo/bar", L"path", L"/foo/bar/path"},
- {L"/foo/bar/", L"path", L"/foo/bar/path"},
- {L"/foo/bar//", L"path", L"/foo/bar//path"},
- {L"/foo/bar/", L"", L"/foo/bar/"},
- {L"/foo/bar", L"", L"/foo/bar/"},
- {L"", L"path", L"/path"},
- {L"", L"", L"/"},
-#endif
-};
-
-static const struct filename_case {
- const wchar_t* path;
- const wchar_t* filename;
-} filename_cases[] = {
-#if defined(OS_WIN)
- {L"c:\\colon\\backslash", L"backslash"},
- {L"c:\\colon\\backslash\\", L""},
- {L"\\\\filename.exe", L"filename.exe"},
- {L"filename.exe", L"filename.exe"},
- {L"", L""},
- {L"\\\\\\", L""},
- {L"c:/colon/backslash", L"backslash"},
- {L"c:/colon/backslash/", L""},
- {L"//////", L""},
- {L"///filename.exe", L"filename.exe"},
-#elif defined(OS_POSIX)
- {L"/foo/bar", L"bar"},
- {L"/foo/bar/", L""},
- {L"/filename.exe", L"filename.exe"},
- {L"filename.exe", L"filename.exe"},
- {L"", L""},
- {L"/", L""},
-#endif
-};
-
-// Test finding the file type from a path name
-static const struct extension_case {
- const wchar_t* path;
- const wchar_t* extension;
-} extension_cases[] = {
-#if defined(OS_WIN)
- {L"C:\\colon\\backslash\\filename.extension", L"extension"},
- {L"C:\\colon\\backslash\\filename.", L""},
- {L"C:\\colon\\backslash\\filename", L""},
- {L"C:\\colon\\backslash\\", L""},
- {L"C:\\colon\\backslash.\\", L""},
- {L"C:\\colon\\backslash\filename.extension.extension2", L"extension2"},
-#elif defined(OS_POSIX)
- {L"/foo/bar/filename.extension", L"extension"},
- {L"/foo/bar/filename.", L""},
- {L"/foo/bar/filename", L""},
- {L"/foo/bar/", L""},
- {L"/foo/bar./", L""},
- {L"/foo/bar/filename.extension.extension2", L"extension2"},
- {L".", L""},
- {L"..", L""},
- {L"./foo", L""},
- {L"./foo.extension", L"extension"},
- {L"/foo.extension1/bar.extension2", L"extension2"},
-#endif
-};
-
-// Test finding the directory component of a path
-static const struct dir_case {
- const wchar_t* full_path;
- const wchar_t* directory;
-} dir_cases[] = {
-#if defined(OS_WIN)
- {L"C:\\WINDOWS\\system32\\gdi32.dll", L"C:\\WINDOWS\\system32"},
- {L"C:\\WINDOWS\\system32\\not_exist_thx_1138", L"C:\\WINDOWS\\system32"},
- {L"C:\\WINDOWS\\system32\\", L"C:\\WINDOWS\\system32"},
- {L"C:\\WINDOWS\\system32\\\\", L"C:\\WINDOWS\\system32"},
- {L"C:\\WINDOWS\\system32", L"C:\\WINDOWS"},
- {L"C:\\WINDOWS\\system32.\\", L"C:\\WINDOWS\\system32."},
- {L"C:\\", L"C:\\"},
-#elif defined(OS_POSIX)
- {L"/foo/bar/gdi32.dll", L"/foo/bar"},
- {L"/foo/bar/not_exist_thx_1138", L"/foo/bar"},
- {L"/foo/bar/", L"/foo/bar"},
- {L"/foo/bar//", L"/foo/bar"},
- {L"/foo/bar", L"/foo"},
- {L"/foo/bar./", L"/foo/bar."},
- {L"/", L"/"},
- {L".", L"."},
- {L"..", L"."}, // yes, ".." technically lives in "."
-#endif
-};
-
TEST_F(FileUtilTest, FileAndDirectorySize) {
// Create three files of 20, 30 and 3 chars (utf8). ComputeDirectorySize
// should return 53 bytes.
FilePath file_01 = temp_dir_.path().Append(FPL("The file 01.txt"));
CreateTextFile(file_01, L"12345678901234567890");
int64 size_f1 = 0;
- ASSERT_TRUE(file_util::GetFileSize(file_01, &size_f1));
+ ASSERT_TRUE(GetFileSize(file_01, &size_f1));
EXPECT_EQ(20ll, size_f1);
FilePath subdir_path = temp_dir_.path().Append(FPL("Level2"));
- file_util::CreateDirectory(subdir_path);
+ CreateDirectory(subdir_path);
FilePath file_02 = subdir_path.Append(FPL("The file 02.txt"));
CreateTextFile(file_02, L"123456789012345678901234567890");
int64 size_f2 = 0;
- ASSERT_TRUE(file_util::GetFileSize(file_02, &size_f2));
+ ASSERT_TRUE(GetFileSize(file_02, &size_f2));
EXPECT_EQ(30ll, size_f2);
FilePath subsubdir_path = subdir_path.Append(FPL("Level3"));
- file_util::CreateDirectory(subsubdir_path);
+ CreateDirectory(subsubdir_path);
FilePath file_03 = subsubdir_path.Append(FPL("The file 03.txt"));
CreateTextFile(file_03, L"123");
- int64 computed_size = base::ComputeDirectorySize(temp_dir_.path());
+ int64 computed_size = ComputeDirectorySize(temp_dir_.path());
EXPECT_EQ(size_f1 + size_f2 + 3, computed_size);
}
@@ -380,23 +278,20 @@ TEST_F(FileUtilTest, NormalizeFilePathBasic) {
FilePath file_a_path = temp_dir_.path().Append(FPL("file_a"));
FilePath dir_path = temp_dir_.path().Append(FPL("dir"));
FilePath file_b_path = dir_path.Append(FPL("file_b"));
- file_util::CreateDirectory(dir_path);
+ CreateDirectory(dir_path);
FilePath normalized_file_a_path, normalized_file_b_path;
- ASSERT_FALSE(base::PathExists(file_a_path));
- ASSERT_FALSE(file_util::NormalizeFilePath(file_a_path,
- &normalized_file_a_path))
+ ASSERT_FALSE(PathExists(file_a_path));
+ ASSERT_FALSE(NormalizeFilePath(file_a_path, &normalized_file_a_path))
<< "NormalizeFilePath() should fail on nonexistent paths.";
CreateTextFile(file_a_path, bogus_content);
- ASSERT_TRUE(base::PathExists(file_a_path));
- ASSERT_TRUE(file_util::NormalizeFilePath(file_a_path,
- &normalized_file_a_path));
+ ASSERT_TRUE(PathExists(file_a_path));
+ ASSERT_TRUE(NormalizeFilePath(file_a_path, &normalized_file_a_path));
CreateTextFile(file_b_path, bogus_content);
- ASSERT_TRUE(base::PathExists(file_b_path));
- ASSERT_TRUE(file_util::NormalizeFilePath(file_b_path,
- &normalized_file_b_path));
+ ASSERT_TRUE(PathExists(file_b_path));
+ ASSERT_TRUE(NormalizeFilePath(file_b_path, &normalized_file_b_path));
// Beacuse this test created |dir_path|, we know it is not a link
// or junction. So, the real path of the directory holding file a
@@ -423,10 +318,10 @@ TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
// |-> to_sub_long (reparse point to temp_dir\sub_a\long_name_\sub_long)
FilePath base_a = temp_dir_.path().Append(FPL("base_a"));
- ASSERT_TRUE(file_util::CreateDirectory(base_a));
+ ASSERT_TRUE(CreateDirectory(base_a));
FilePath sub_a = base_a.Append(FPL("sub_a"));
- ASSERT_TRUE(file_util::CreateDirectory(sub_a));
+ ASSERT_TRUE(CreateDirectory(sub_a));
FilePath file_txt = sub_a.Append(FPL("file.txt"));
CreateTextFile(file_txt, bogus_content);
@@ -454,42 +349,42 @@ TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
ASSERT_EQ(MAX_PATH - kCreateDirLimit, deep_file.value().length());
FilePath sub_long = deep_file.DirName();
- ASSERT_TRUE(file_util::CreateDirectory(sub_long));
+ ASSERT_TRUE(CreateDirectory(sub_long));
CreateTextFile(deep_file, bogus_content);
FilePath base_b = temp_dir_.path().Append(FPL("base_b"));
- ASSERT_TRUE(file_util::CreateDirectory(base_b));
+ ASSERT_TRUE(CreateDirectory(base_b));
FilePath to_sub_a = base_b.Append(FPL("to_sub_a"));
- ASSERT_TRUE(file_util::CreateDirectory(to_sub_a));
+ ASSERT_TRUE(CreateDirectory(to_sub_a));
FilePath normalized_path;
{
ReparsePoint reparse_to_sub_a(to_sub_a, sub_a);
ASSERT_TRUE(reparse_to_sub_a.IsValid());
FilePath to_base_b = base_b.Append(FPL("to_base_b"));
- ASSERT_TRUE(file_util::CreateDirectory(to_base_b));
+ ASSERT_TRUE(CreateDirectory(to_base_b));
ReparsePoint reparse_to_base_b(to_base_b, base_b);
ASSERT_TRUE(reparse_to_base_b.IsValid());
FilePath to_sub_long = base_b.Append(FPL("to_sub_long"));
- ASSERT_TRUE(file_util::CreateDirectory(to_sub_long));
+ ASSERT_TRUE(CreateDirectory(to_sub_long));
ReparsePoint reparse_to_sub_long(to_sub_long, sub_long);
ASSERT_TRUE(reparse_to_sub_long.IsValid());
// Normalize a junction free path: base_a\sub_a\file.txt .
- ASSERT_TRUE(file_util::NormalizeFilePath(file_txt, &normalized_path));
+ ASSERT_TRUE(NormalizeFilePath(file_txt, &normalized_path));
ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
// Check that the path base_b\to_sub_a\file.txt can be normalized to exclude
// the junction to_sub_a.
- ASSERT_TRUE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+ ASSERT_TRUE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
&normalized_path));
ASSERT_STREQ(file_txt.value().c_str(), normalized_path.value().c_str());
// Check that the path base_b\to_base_b\to_base_b\to_sub_a\file.txt can be
// normalized to exclude junctions to_base_b and to_sub_a .
- ASSERT_TRUE(file_util::NormalizeFilePath(base_b.Append(FPL("to_base_b"))
+ ASSERT_TRUE(NormalizeFilePath(base_b.Append(FPL("to_base_b"))
.Append(FPL("to_base_b"))
.Append(FPL("to_sub_a"))
.Append(FPL("file.txt")),
@@ -507,18 +402,18 @@ TEST_F(FileUtilTest, NormalizeFilePathReparsePoints) {
long_path = long_path.Append(FPL("to_sub_a"))
.Append(FPL("file.txt"));
- ASSERT_FALSE(file_util::NormalizeFilePath(long_path, &normalized_path));
+ ASSERT_FALSE(NormalizeFilePath(long_path, &normalized_path));
// Normalizing the junction to deep.txt should fail, because the expanded
// path to deep.txt is longer than MAX_PATH.
- ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_long.Append(deep_txt),
+ ASSERT_FALSE(NormalizeFilePath(to_sub_long.Append(deep_txt),
&normalized_path));
// Delete the reparse points, and see that NormalizeFilePath() fails
// to traverse them.
}
- ASSERT_FALSE(file_util::NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
+ ASSERT_FALSE(NormalizeFilePath(to_sub_a.Append(FPL("file.txt")),
&normalized_path));
}
@@ -539,14 +434,13 @@ TEST_F(FileUtilTest, DevicePathToDriveLetter) {
// Run DevicePathToDriveLetterPath() on the NT style path we got from
// QueryDosDevice(). Expect the drive letter we started with.
- ASSERT_TRUE(file_util::DevicePathToDriveLetterPath(actual_device_path,
- &win32_path));
+ ASSERT_TRUE(DevicePathToDriveLetterPath(actual_device_path, &win32_path));
ASSERT_EQ(real_drive_letter, win32_path.value());
// Add some directories to the path. Expect those extra path componenets
// to be preserved.
FilePath kRelativePath(FPL("dir1\\dir2\\file.txt"));
- ASSERT_TRUE(file_util::DevicePathToDriveLetterPath(
+ ASSERT_TRUE(DevicePathToDriveLetterPath(
actual_device_path.Append(kRelativePath),
&win32_path));
EXPECT_EQ(FilePath(real_drive_letter + L"\\").Append(kRelativePath).value(),
@@ -564,11 +458,10 @@ TEST_F(FileUtilTest, DevicePathToDriveLetter) {
ASSERT_LT(0, new_length);
FilePath prefix_of_real_device_path(
actual_device_path.value().substr(0, new_length));
- ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
- prefix_of_real_device_path,
- &win32_path));
+ ASSERT_FALSE(DevicePathToDriveLetterPath(prefix_of_real_device_path,
+ &win32_path));
- ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ ASSERT_FALSE(DevicePathToDriveLetterPath(
prefix_of_real_device_path.Append(kRelativePath),
&win32_path));
@@ -583,19 +476,19 @@ TEST_F(FileUtilTest, DevicePathToDriveLetter) {
FilePath real_device_path_plus_numbers(
actual_device_path.value() + kExtraChars);
- ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ ASSERT_FALSE(DevicePathToDriveLetterPath(
real_device_path_plus_numbers,
&win32_path));
- ASSERT_FALSE(file_util::DevicePathToDriveLetterPath(
+ ASSERT_FALSE(DevicePathToDriveLetterPath(
real_device_path_plus_numbers.Append(kRelativePath),
&win32_path));
}
TEST_F(FileUtilTest, GetPlatformFileInfoForDirectory) {
FilePath empty_dir = temp_dir_.path().Append(FPL("gpfi_test"));
- ASSERT_TRUE(file_util::CreateDirectory(empty_dir));
- base::win::ScopedHandle dir(
+ 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,
@@ -604,8 +497,8 @@ TEST_F(FileUtilTest, GetPlatformFileInfoForDirectory) {
FILE_FLAG_BACKUP_SEMANTICS, // Needed to open a directory.
NULL));
ASSERT_TRUE(dir.IsValid());
- base::PlatformFileInfo info;
- EXPECT_TRUE(base::GetPlatformFileInfo(dir.Get(), &info));
+ PlatformFileInfo info;
+ EXPECT_TRUE(GetPlatformFileInfo(dir.Get(), &info));
EXPECT_TRUE(info.is_directory);
EXPECT_FALSE(info.is_symbolic_link);
EXPECT_EQ(0, info.size);
@@ -620,7 +513,7 @@ TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) {
const FilePath::CharType kLongDirName[] = FPL("A long path");
const FilePath::CharType kTestSubDirName[] = FPL("test");
FilePath long_test_dir = temp_dir_.path().Append(kLongDirName);
- ASSERT_TRUE(file_util::CreateDirectory(long_test_dir));
+ ASSERT_TRUE(CreateDirectory(long_test_dir));
// kLongDirName is not a 8.3 component. So GetShortName() should give us a
// different short name.
@@ -633,9 +526,9 @@ TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) {
ASSERT_STRNE(kLongDirName, short_test_dir.BaseName().value().c_str());
FilePath temp_file;
- ASSERT_TRUE(file_util::CreateTemporaryFileInDir(short_test_dir, &temp_file));
+ ASSERT_TRUE(CreateTemporaryFileInDir(short_test_dir, &temp_file));
EXPECT_STREQ(kLongDirName, temp_file.DirName().BaseName().value().c_str());
- EXPECT_TRUE(base::PathExists(temp_file));
+ EXPECT_TRUE(PathExists(temp_file));
// Create a subdirectory of |long_test_dir| and make |long_test_dir|
// unreadable. We should still be able to create a temp file in the
@@ -645,14 +538,14 @@ TEST_F(FileUtilTest, CreateTemporaryFileInDirLongPathTest) {
// directories. (Note that this assumption is true for NTFS, but not for some
// network file systems. E.g. AFS).
FilePath access_test_dir = long_test_dir.Append(kTestSubDirName);
- ASSERT_TRUE(file_util::CreateDirectory(access_test_dir));
+ ASSERT_TRUE(CreateDirectory(access_test_dir));
file_util::PermissionRestorer long_test_dir_restorer(long_test_dir);
ASSERT_TRUE(file_util::MakeFileUnreadable(long_test_dir));
// Use the short form of the directory to create a temporary filename.
- ASSERT_TRUE(file_util::CreateTemporaryFileInDir(
+ ASSERT_TRUE(CreateTemporaryFileInDir(
short_test_dir.Append(kTestSubDirName), &temp_file));
- EXPECT_TRUE(base::PathExists(temp_file));
+ EXPECT_TRUE(PathExists(temp_file));
EXPECT_TRUE(short_test_dir.IsParent(temp_file.DirName()));
// Check that the long path can't be determined for |temp_file|.
@@ -670,7 +563,7 @@ TEST_F(FileUtilTest, CreateAndReadSymlinks) {
FilePath link_to = temp_dir_.path().Append(FPL("to_file"));
CreateTextFile(link_to, bogus_content);
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
<< "Failed to create file symlink.";
// If we created the link properly, we should be able to read the contents
@@ -679,21 +572,21 @@ TEST_F(FileUtilTest, CreateAndReadSymlinks) {
EXPECT_EQ(bogus_content, contents);
FilePath result;
- ASSERT_TRUE(file_util::ReadSymbolicLink(link_from, &result));
+ ASSERT_TRUE(ReadSymbolicLink(link_from, &result));
EXPECT_EQ(link_to.value(), result.value());
// Link to a directory.
link_from = temp_dir_.path().Append(FPL("from_dir"));
link_to = temp_dir_.path().Append(FPL("to_dir"));
- ASSERT_TRUE(file_util::CreateDirectory(link_to));
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ ASSERT_TRUE(CreateDirectory(link_to));
+ ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
<< "Failed to create directory symlink.";
// Test failures.
- EXPECT_FALSE(file_util::CreateSymbolicLink(link_to, link_to));
- EXPECT_FALSE(file_util::ReadSymbolicLink(link_to, &result));
+ EXPECT_FALSE(CreateSymbolicLink(link_to, link_to));
+ EXPECT_FALSE(ReadSymbolicLink(link_to, &result));
FilePath missing = temp_dir_.path().Append(FPL("missing"));
- EXPECT_FALSE(file_util::ReadSymbolicLink(missing, &result));
+ EXPECT_FALSE(ReadSymbolicLink(missing, &result));
}
// The following test of NormalizeFilePath() require that we create a symlink.
@@ -707,12 +600,12 @@ TEST_F(FileUtilTest, NormalizeFilePathSymlinks) {
FilePath link_to = temp_dir_.path().Append(FPL("to_file"));
CreateTextFile(link_to, bogus_content);
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
<< "Failed to create file symlink.";
// Check that NormalizeFilePath sees the link.
FilePath normalized_path;
- ASSERT_TRUE(file_util::NormalizeFilePath(link_from, &normalized_path));
+ ASSERT_TRUE(NormalizeFilePath(link_from, &normalized_path));
EXPECT_NE(link_from, link_to);
EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
EXPECT_EQ(link_to.BaseName().value(), normalized_path.BaseName().value());
@@ -720,54 +613,54 @@ TEST_F(FileUtilTest, NormalizeFilePathSymlinks) {
// Link to a directory.
link_from = temp_dir_.path().Append(FPL("from_dir"));
link_to = temp_dir_.path().Append(FPL("to_dir"));
- ASSERT_TRUE(file_util::CreateDirectory(link_to));
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ ASSERT_TRUE(CreateDirectory(link_to));
+ ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
<< "Failed to create directory symlink.";
- EXPECT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path))
+ EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path))
<< "Links to directories should return false.";
// Test that a loop in the links causes NormalizeFilePath() to return false.
link_from = temp_dir_.path().Append(FPL("link_a"));
link_to = temp_dir_.path().Append(FPL("link_b"));
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_to, link_from))
+ ASSERT_TRUE(CreateSymbolicLink(link_to, link_from))
<< "Failed to create loop symlink a.";
- ASSERT_TRUE(file_util::CreateSymbolicLink(link_from, link_to))
+ ASSERT_TRUE(CreateSymbolicLink(link_from, link_to))
<< "Failed to create loop symlink b.";
// Infinite loop!
- EXPECT_FALSE(file_util::NormalizeFilePath(link_from, &normalized_path));
+ EXPECT_FALSE(NormalizeFilePath(link_from, &normalized_path));
}
#endif // defined(OS_POSIX)
TEST_F(FileUtilTest, DeleteNonExistent) {
FilePath non_existent = temp_dir_.path().AppendASCII("bogus_file_dne.foobar");
- ASSERT_FALSE(base::PathExists(non_existent));
+ ASSERT_FALSE(PathExists(non_existent));
- EXPECT_TRUE(base::DeleteFile(non_existent, false));
- ASSERT_FALSE(base::PathExists(non_existent));
- EXPECT_TRUE(base::DeleteFile(non_existent, true));
- ASSERT_FALSE(base::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"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
// Make sure it's deleted
- EXPECT_TRUE(base::DeleteFile(file_name, false));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_TRUE(DeleteFile(file_name, false));
+ EXPECT_FALSE(PathExists(file_name));
// Test recursive case, create a new file
file_name = temp_dir_.path().Append(FPL("Test DeleteFile 2.txt"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
// Make sure it's deleted
- EXPECT_TRUE(base::DeleteFile(file_name, true));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_TRUE(DeleteFile(file_name, true));
+ EXPECT_FALSE(PathExists(file_name));
}
#if defined(OS_POSIX)
@@ -775,46 +668,46 @@ TEST_F(FileUtilTest, DeleteSymlinkToExistentFile) {
// Create a file.
FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteFile 2.txt"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
// Create a symlink to the file.
FilePath file_link = temp_dir_.path().Append("file_link_2");
- ASSERT_TRUE(file_util::CreateSymbolicLink(file_name, file_link))
+ ASSERT_TRUE(CreateSymbolicLink(file_name, file_link))
<< "Failed to create symlink.";
// Delete the symbolic link.
- EXPECT_TRUE(base::DeleteFile(file_link, false));
+ EXPECT_TRUE(DeleteFile(file_link, false));
// Make sure original file is not deleted.
- EXPECT_FALSE(base::PathExists(file_link));
- EXPECT_TRUE(base::PathExists(file_name));
+ EXPECT_FALSE(PathExists(file_link));
+ EXPECT_TRUE(PathExists(file_name));
}
TEST_F(FileUtilTest, DeleteSymlinkToNonExistentFile) {
// Create a non-existent file path.
FilePath non_existent = temp_dir_.path().Append(FPL("Test DeleteFile 3.txt"));
- EXPECT_FALSE(base::PathExists(non_existent));
+ EXPECT_FALSE(PathExists(non_existent));
// Create a symlink to the non-existent file.
FilePath file_link = temp_dir_.path().Append("file_link_3");
- ASSERT_TRUE(file_util::CreateSymbolicLink(non_existent, file_link))
+ ASSERT_TRUE(CreateSymbolicLink(non_existent, file_link))
<< "Failed to create symlink.";
// Make sure the symbolic link is exist.
- EXPECT_TRUE(file_util::IsLink(file_link));
- EXPECT_FALSE(base::PathExists(file_link));
+ EXPECT_TRUE(IsLink(file_link));
+ EXPECT_FALSE(PathExists(file_link));
// Delete the symbolic link.
- EXPECT_TRUE(base::DeleteFile(file_link, false));
+ EXPECT_TRUE(DeleteFile(file_link, false));
// Make sure the symbolic link is deleted.
- EXPECT_FALSE(file_util::IsLink(file_link));
+ EXPECT_FALSE(IsLink(file_link));
}
TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
// Create a file path.
FilePath file_name = temp_dir_.path().Append(FPL("Test Readable File.txt"));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_FALSE(PathExists(file_name));
const std::string kData("hello");
@@ -824,33 +717,31 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
// Write file.
EXPECT_EQ(static_cast<int>(kData.length()),
file_util::WriteFile(file_name, kData.data(), kData.length()));
- EXPECT_TRUE(base::PathExists(file_name));
+ EXPECT_TRUE(PathExists(file_name));
// Make sure the file is readable.
int32 mode = 0;
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_TRUE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+ EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER);
// Get rid of the read permission.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(file_name, 0u));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_FALSE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+ EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u));
+ EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+ EXPECT_FALSE(mode & FILE_PERMISSION_READ_BY_USER);
// Make sure the file can't be read.
- EXPECT_EQ(-1, file_util::ReadFile(file_name, buffer, buffer_size));
+ EXPECT_EQ(-1, ReadFile(file_name, buffer, buffer_size));
// Give the read permission.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(
- file_name,
- file_util::FILE_PERMISSION_READ_BY_USER));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_TRUE(mode & file_util::FILE_PERMISSION_READ_BY_USER);
+ EXPECT_TRUE(SetPosixFilePermissions(file_name, FILE_PERMISSION_READ_BY_USER));
+ EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & FILE_PERMISSION_READ_BY_USER);
// Make sure the file can be read.
EXPECT_EQ(static_cast<int>(kData.length()),
- file_util::ReadFile(file_name, buffer, buffer_size));
+ ReadFile(file_name, buffer, buffer_size));
// Delete the file.
- EXPECT_TRUE(base::DeleteFile(file_name, false));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_TRUE(DeleteFile(file_name, false));
+ EXPECT_FALSE(PathExists(file_name));
delete[] buffer;
}
@@ -858,86 +749,81 @@ TEST_F(FileUtilTest, ChangeFilePermissionsAndRead) {
TEST_F(FileUtilTest, ChangeFilePermissionsAndWrite) {
// Create a file path.
FilePath file_name = temp_dir_.path().Append(FPL("Test Readable File.txt"));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_FALSE(PathExists(file_name));
const std::string kData("hello");
// Write file.
EXPECT_EQ(static_cast<int>(kData.length()),
file_util::WriteFile(file_name, kData.data(), kData.length()));
- EXPECT_TRUE(base::PathExists(file_name));
+ EXPECT_TRUE(PathExists(file_name));
// Make sure the file is writable.
int mode = 0;
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_TRUE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+ EXPECT_TRUE(mode & FILE_PERMISSION_WRITE_BY_USER);
EXPECT_TRUE(PathIsWritable(file_name));
// Get rid of the write permission.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(file_name, 0u));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_FALSE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ EXPECT_TRUE(SetPosixFilePermissions(file_name, 0u));
+ 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_FALSE(PathIsWritable(file_name));
// Give read permission.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(
- file_name,
- file_util::FILE_PERMISSION_WRITE_BY_USER));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(file_name, &mode));
- EXPECT_TRUE(mode & file_util::FILE_PERMISSION_WRITE_BY_USER);
+ EXPECT_TRUE(SetPosixFilePermissions(file_name,
+ FILE_PERMISSION_WRITE_BY_USER));
+ EXPECT_TRUE(GetPosixFilePermissions(file_name, &mode));
+ 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()));
EXPECT_TRUE(PathIsWritable(file_name));
// Delete the file.
- EXPECT_TRUE(base::DeleteFile(file_name, false));
- EXPECT_FALSE(base::PathExists(file_name));
+ EXPECT_TRUE(DeleteFile(file_name, false));
+ EXPECT_FALSE(PathExists(file_name));
}
TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
// Create a directory path.
FilePath subdir_path =
temp_dir_.path().Append(FPL("PermissionTest1"));
- file_util::CreateDirectory(subdir_path);
- ASSERT_TRUE(base::PathExists(subdir_path));
+ CreateDirectory(subdir_path);
+ ASSERT_TRUE(PathExists(subdir_path));
// Create a dummy file to enumerate.
FilePath file_name = subdir_path.Append(FPL("Test Readable File.txt"));
- EXPECT_FALSE(base::PathExists(file_name));
+ 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()));
- EXPECT_TRUE(base::PathExists(file_name));
+ EXPECT_TRUE(PathExists(file_name));
// Make sure the directory has the all permissions.
int mode = 0;
- EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
- EXPECT_EQ(file_util::FILE_PERMISSION_USER_MASK,
- mode & file_util::FILE_PERMISSION_USER_MASK);
+ EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK);
// Get rid of the permissions from the directory.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(subdir_path, 0u));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
- EXPECT_FALSE(mode & file_util::FILE_PERMISSION_USER_MASK);
+ EXPECT_TRUE(SetPosixFilePermissions(subdir_path, 0u));
+ EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_FALSE(mode & FILE_PERMISSION_USER_MASK);
// Make sure the file in the directory can't be enumerated.
FileEnumerator f1(subdir_path, true, FileEnumerator::FILES);
- EXPECT_TRUE(base::PathExists(subdir_path));
+ EXPECT_TRUE(PathExists(subdir_path));
FindResultCollector c1(f1);
EXPECT_EQ(c1.size(), 0);
- EXPECT_FALSE(file_util::GetPosixFilePermissions(file_name, &mode));
+ EXPECT_FALSE(GetPosixFilePermissions(file_name, &mode));
// Give the permissions to the directory.
- EXPECT_TRUE(file_util::SetPosixFilePermissions(
- subdir_path,
- file_util::FILE_PERMISSION_USER_MASK));
- EXPECT_TRUE(file_util::GetPosixFilePermissions(subdir_path, &mode));
- EXPECT_EQ(file_util::FILE_PERMISSION_USER_MASK,
- mode & file_util::FILE_PERMISSION_USER_MASK);
+ EXPECT_TRUE(SetPosixFilePermissions(subdir_path, FILE_PERMISSION_USER_MASK));
+ EXPECT_TRUE(GetPosixFilePermissions(subdir_path, &mode));
+ EXPECT_EQ(FILE_PERMISSION_USER_MASK, mode & FILE_PERMISSION_USER_MASK);
// Make sure the file in the directory can be enumerated.
FileEnumerator f2(subdir_path, true, FileEnumerator::FILES);
@@ -946,8 +832,8 @@ TEST_F(FileUtilTest, ChangeDirectoryPermissionsAndEnumerate) {
EXPECT_EQ(c2.size(), 1);
// Delete the file.
- EXPECT_TRUE(base::DeleteFile(subdir_path, true));
- EXPECT_FALSE(base::PathExists(subdir_path));
+ EXPECT_TRUE(DeleteFile(subdir_path, true));
+ EXPECT_FALSE(PathExists(subdir_path));
}
#endif // defined(OS_POSIX)
@@ -960,25 +846,25 @@ TEST_F(FileUtilTest, DeleteWildCard) {
// Create a file and a directory
FilePath file_name = temp_dir_.path().Append(FPL("Test DeleteWildCard.txt"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
FilePath subdir_path = temp_dir_.path().Append(FPL("DeleteWildCardDir"));
- file_util::CreateDirectory(subdir_path);
- ASSERT_TRUE(base::PathExists(subdir_path));
+ CreateDirectory(subdir_path);
+ ASSERT_TRUE(PathExists(subdir_path));
// Create the wildcard path
FilePath directory_contents = temp_dir_.path();
directory_contents = directory_contents.Append(FPL("*"));
// Delete non-recursively and check that only the file is deleted
- EXPECT_TRUE(base::DeleteFile(directory_contents, false));
- EXPECT_FALSE(base::PathExists(file_name));
- EXPECT_TRUE(base::PathExists(subdir_path));
+ EXPECT_TRUE(DeleteFile(directory_contents, false));
+ EXPECT_FALSE(PathExists(file_name));
+ EXPECT_TRUE(PathExists(subdir_path));
// Delete recursively and make sure all contents are deleted
- EXPECT_TRUE(base::DeleteFile(directory_contents, true));
- EXPECT_FALSE(base::PathExists(file_name));
- EXPECT_FALSE(base::PathExists(subdir_path));
+ EXPECT_TRUE(DeleteFile(directory_contents, true));
+ EXPECT_FALSE(PathExists(file_name));
+ EXPECT_FALSE(PathExists(subdir_path));
}
// TODO(erikkay): see if anyone's actually using this feature of the API
@@ -986,20 +872,20 @@ TEST_F(FileUtilTest, DeleteNonExistantWildCard) {
// Create a file and a directory
FilePath subdir_path =
temp_dir_.path().Append(FPL("DeleteNonExistantWildCard"));
- file_util::CreateDirectory(subdir_path);
- ASSERT_TRUE(base::PathExists(subdir_path));
+ CreateDirectory(subdir_path);
+ ASSERT_TRUE(PathExists(subdir_path));
// Create the wildcard path
FilePath directory_contents = subdir_path;
directory_contents = directory_contents.Append(FPL("*"));
// Delete non-recursively and check nothing got deleted
- EXPECT_TRUE(base::DeleteFile(directory_contents, false));
- EXPECT_TRUE(base::PathExists(subdir_path));
+ EXPECT_TRUE(DeleteFile(directory_contents, false));
+ EXPECT_TRUE(PathExists(subdir_path));
// Delete recursively and check nothing got deleted
- EXPECT_TRUE(base::DeleteFile(directory_contents, true));
- EXPECT_TRUE(base::PathExists(subdir_path));
+ EXPECT_TRUE(DeleteFile(directory_contents, true));
+ EXPECT_TRUE(PathExists(subdir_path));
}
#endif
@@ -1007,60 +893,60 @@ TEST_F(FileUtilTest, DeleteNonExistantWildCard) {
TEST_F(FileUtilTest, DeleteDirNonRecursive) {
// Create a subdirectory and put a file and two directories inside.
FilePath test_subdir = temp_dir_.path().Append(FPL("DeleteDirNonRecursive"));
- file_util::CreateDirectory(test_subdir);
- ASSERT_TRUE(base::PathExists(test_subdir));
+ CreateDirectory(test_subdir);
+ ASSERT_TRUE(PathExists(test_subdir));
FilePath file_name = test_subdir.Append(FPL("Test DeleteDir.txt"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
- file_util::CreateDirectory(subdir_path1);
- ASSERT_TRUE(base::PathExists(subdir_path1));
+ CreateDirectory(subdir_path1);
+ ASSERT_TRUE(PathExists(subdir_path1));
FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
- file_util::CreateDirectory(subdir_path2);
- ASSERT_TRUE(base::PathExists(subdir_path2));
+ CreateDirectory(subdir_path2);
+ ASSERT_TRUE(PathExists(subdir_path2));
// Delete non-recursively and check that the empty dir got deleted
- EXPECT_TRUE(base::DeleteFile(subdir_path2, false));
- EXPECT_FALSE(base::PathExists(subdir_path2));
+ EXPECT_TRUE(DeleteFile(subdir_path2, false));
+ EXPECT_FALSE(PathExists(subdir_path2));
// Delete non-recursively and check that nothing got deleted
- EXPECT_FALSE(base::DeleteFile(test_subdir, false));
- EXPECT_TRUE(base::PathExists(test_subdir));
- EXPECT_TRUE(base::PathExists(file_name));
- EXPECT_TRUE(base::PathExists(subdir_path1));
+ EXPECT_FALSE(DeleteFile(test_subdir, false));
+ EXPECT_TRUE(PathExists(test_subdir));
+ EXPECT_TRUE(PathExists(file_name));
+ EXPECT_TRUE(PathExists(subdir_path1));
}
// Tests recursive Delete() for a directory.
TEST_F(FileUtilTest, DeleteDirRecursive) {
// Create a subdirectory and put a file and two directories inside.
FilePath test_subdir = temp_dir_.path().Append(FPL("DeleteDirRecursive"));
- file_util::CreateDirectory(test_subdir);
- ASSERT_TRUE(base::PathExists(test_subdir));
+ CreateDirectory(test_subdir);
+ ASSERT_TRUE(PathExists(test_subdir));
FilePath file_name = test_subdir.Append(FPL("Test DeleteDirRecursive.txt"));
CreateTextFile(file_name, bogus_content);
- ASSERT_TRUE(base::PathExists(file_name));
+ ASSERT_TRUE(PathExists(file_name));
FilePath subdir_path1 = test_subdir.Append(FPL("TestSubDir1"));
- file_util::CreateDirectory(subdir_path1);
- ASSERT_TRUE(base::PathExists(subdir_path1));
+ CreateDirectory(subdir_path1);
+ ASSERT_TRUE(PathExists(subdir_path1));
FilePath subdir_path2 = test_subdir.Append(FPL("TestSubDir2"));
- file_util::CreateDirectory(subdir_path2);
- ASSERT_TRUE(base::PathExists(subdir_path2));
+ CreateDirectory(subdir_path2);
+ ASSERT_TRUE(PathExists(subdir_path2));
// Delete recursively and check that the empty dir got deleted
- EXPECT_TRUE(base::DeleteFile(subdir_path2, true));
- EXPECT_FALSE(base::PathExists(subdir_path2));
+ EXPECT_TRUE(DeleteFile(subdir_path2, true));
+ EXPECT_FALSE(PathExists(subdir_path2));
// Delete recursively and check that everything got deleted
- EXPECT_TRUE(base::DeleteFile(test_subdir, true));
- EXPECT_FALSE(base::PathExists(file_name));
- EXPECT_FALSE(base::PathExists(subdir_path1));
- EXPECT_FALSE(base::PathExists(test_subdir));
+ EXPECT_TRUE(DeleteFile(test_subdir, true));
+ EXPECT_FALSE(PathExists(file_name));
+ EXPECT_FALSE(PathExists(subdir_path1));
+ EXPECT_FALSE(PathExists(test_subdir));
}
TEST_F(FileUtilTest, MoveFileNew) {
@@ -1068,18 +954,18 @@ TEST_F(FileUtilTest, MoveFileNew) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination.
FilePath file_name_to = temp_dir_.path().Append(
FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
- ASSERT_FALSE(base::PathExists(file_name_to));
+ ASSERT_FALSE(PathExists(file_name_to));
- EXPECT_TRUE(base::Move(file_name_from, file_name_to));
+ EXPECT_TRUE(Move(file_name_from, file_name_to));
// Check everything has been moved.
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, MoveFileExists) {
@@ -1087,19 +973,19 @@ TEST_F(FileUtilTest, MoveFileExists) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination name.
FilePath file_name_to = temp_dir_.path().Append(
FILE_PATH_LITERAL("Move_Test_File_Destination.txt"));
CreateTextFile(file_name_to, L"Old file content");
- ASSERT_TRUE(base::PathExists(file_name_to));
+ ASSERT_TRUE(PathExists(file_name_to));
- EXPECT_TRUE(base::Move(file_name_from, file_name_to));
+ EXPECT_TRUE(Move(file_name_from, file_name_to));
// Check everything has been moved.
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(file_name_to));
EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
}
@@ -1108,15 +994,15 @@ TEST_F(FileUtilTest, MoveFileDirExists) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination directory
FilePath dir_name_to =
temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
- file_util::CreateDirectory(dir_name_to);
- ASSERT_TRUE(base::PathExists(dir_name_to));
+ CreateDirectory(dir_name_to);
+ ASSERT_TRUE(PathExists(dir_name_to));
- EXPECT_FALSE(base::Move(file_name_from, dir_name_to));
+ EXPECT_FALSE(Move(file_name_from, dir_name_to));
}
@@ -1124,14 +1010,14 @@ TEST_F(FileUtilTest, MoveNew) {
// Create a directory
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory
FilePath txt_file_name(FILE_PATH_LITERAL("Move_Test_File.txt"));
FilePath file_name_from = dir_name_from.Append(txt_file_name);
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Move the directory.
FilePath dir_name_to =
@@ -1139,40 +1025,40 @@ TEST_F(FileUtilTest, MoveNew) {
FilePath file_name_to =
dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
- ASSERT_FALSE(base::PathExists(dir_name_to));
+ ASSERT_FALSE(PathExists(dir_name_to));
- EXPECT_TRUE(base::Move(dir_name_from, dir_name_to));
+ EXPECT_TRUE(Move(dir_name_from, dir_name_to));
// Check everything has been moved.
- EXPECT_FALSE(base::PathExists(dir_name_from));
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(dir_name_from));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
// Test path traversal.
file_name_from = dir_name_to.Append(txt_file_name);
file_name_to = dir_name_to.Append(FILE_PATH_LITERAL(".."));
file_name_to = file_name_to.Append(txt_file_name);
- EXPECT_FALSE(base::Move(file_name_from, file_name_to));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_FALSE(base::PathExists(file_name_to));
- EXPECT_TRUE(base::internal::MoveUnsafe(file_name_from, file_name_to));
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(Move(file_name_from, file_name_to));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_FALSE(PathExists(file_name_to));
+ EXPECT_TRUE(internal::MoveUnsafe(file_name_from, file_name_to));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, MoveExist) {
// Create a directory
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Move_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Move the directory
FilePath dir_name_exists =
@@ -1184,42 +1070,42 @@ TEST_F(FileUtilTest, MoveExist) {
dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt"));
// Create the destination directory.
- file_util::CreateDirectory(dir_name_exists);
- ASSERT_TRUE(base::PathExists(dir_name_exists));
+ CreateDirectory(dir_name_exists);
+ ASSERT_TRUE(PathExists(dir_name_exists));
- EXPECT_TRUE(base::Move(dir_name_from, dir_name_to));
+ EXPECT_TRUE(Move(dir_name_from, dir_name_to));
// Check everything has been moved.
- EXPECT_FALSE(base::PathExists(dir_name_from));
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(dir_name_from));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, CopyDirectoryRecursivelyNew) {
// Create a directory.
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory.
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Create a subdirectory.
FilePath subdir_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
- file_util::CreateDirectory(subdir_name_from);
- ASSERT_TRUE(base::PathExists(subdir_name_from));
+ CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(PathExists(subdir_name_from));
// Create a file under the subdirectory.
FilePath file_name2_from =
subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name2_from));
+ ASSERT_TRUE(PathExists(file_name2_from));
// Copy the directory recursively.
FilePath dir_name_to =
@@ -1231,45 +1117,45 @@ TEST_F(FileUtilTest, CopyDirectoryRecursivelyNew) {
FilePath file_name2_to =
subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
- ASSERT_FALSE(base::PathExists(dir_name_to));
+ ASSERT_FALSE(PathExists(dir_name_to));
- EXPECT_TRUE(base::CopyDirectory(dir_name_from, dir_name_to, true));
+ EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, true));
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(dir_name_from));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(subdir_name_from));
- EXPECT_TRUE(base::PathExists(file_name2_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
- EXPECT_TRUE(base::PathExists(subdir_name_to));
- EXPECT_TRUE(base::PathExists(file_name2_to));
+ EXPECT_TRUE(PathExists(dir_name_from));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(subdir_name_from));
+ EXPECT_TRUE(PathExists(file_name2_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(subdir_name_to));
+ EXPECT_TRUE(PathExists(file_name2_to));
}
TEST_F(FileUtilTest, CopyDirectoryRecursivelyExists) {
// Create a directory.
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory.
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Create a subdirectory.
FilePath subdir_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
- file_util::CreateDirectory(subdir_name_from);
- ASSERT_TRUE(base::PathExists(subdir_name_from));
+ CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(PathExists(subdir_name_from));
// Create a file under the subdirectory.
FilePath file_name2_from =
subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name2_from));
+ ASSERT_TRUE(PathExists(file_name2_from));
// Copy the directory recursively.
FilePath dir_name_exists =
@@ -1285,46 +1171,46 @@ TEST_F(FileUtilTest, CopyDirectoryRecursivelyExists) {
subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
// Create the destination directory.
- file_util::CreateDirectory(dir_name_exists);
- ASSERT_TRUE(base::PathExists(dir_name_exists));
+ CreateDirectory(dir_name_exists);
+ ASSERT_TRUE(PathExists(dir_name_exists));
- EXPECT_TRUE(base::CopyDirectory(dir_name_from, dir_name_exists, true));
+ EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_exists, true));
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(dir_name_from));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(subdir_name_from));
- EXPECT_TRUE(base::PathExists(file_name2_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
- EXPECT_TRUE(base::PathExists(subdir_name_to));
- EXPECT_TRUE(base::PathExists(file_name2_to));
+ EXPECT_TRUE(PathExists(dir_name_from));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(subdir_name_from));
+ EXPECT_TRUE(PathExists(file_name2_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(subdir_name_to));
+ EXPECT_TRUE(PathExists(file_name2_to));
}
TEST_F(FileUtilTest, CopyDirectoryNew) {
// Create a directory.
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory.
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Create a subdirectory.
FilePath subdir_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
- file_util::CreateDirectory(subdir_name_from);
- ASSERT_TRUE(base::PathExists(subdir_name_from));
+ CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(PathExists(subdir_name_from));
// Create a file under the subdirectory.
FilePath file_name2_from =
subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name2_from));
+ ASSERT_TRUE(PathExists(file_name2_from));
// Copy the directory not recursively.
FilePath dir_name_to =
@@ -1334,44 +1220,44 @@ TEST_F(FileUtilTest, CopyDirectoryNew) {
FilePath subdir_name_to =
dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
- ASSERT_FALSE(base::PathExists(dir_name_to));
+ ASSERT_FALSE(PathExists(dir_name_to));
- EXPECT_TRUE(base::CopyDirectory(dir_name_from, dir_name_to, false));
+ EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(dir_name_from));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(subdir_name_from));
- EXPECT_TRUE(base::PathExists(file_name2_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
- EXPECT_FALSE(base::PathExists(subdir_name_to));
+ EXPECT_TRUE(PathExists(dir_name_from));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(subdir_name_from));
+ EXPECT_TRUE(PathExists(file_name2_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(subdir_name_to));
}
TEST_F(FileUtilTest, CopyDirectoryExists) {
// Create a directory.
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory.
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Create a subdirectory.
FilePath subdir_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Subdir"));
- file_util::CreateDirectory(subdir_name_from);
- ASSERT_TRUE(base::PathExists(subdir_name_from));
+ CreateDirectory(subdir_name_from);
+ ASSERT_TRUE(PathExists(subdir_name_from));
// Create a file under the subdirectory.
FilePath file_name2_from =
subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name2_from));
+ ASSERT_TRUE(PathExists(file_name2_from));
// Copy the directory not recursively.
FilePath dir_name_to =
@@ -1382,19 +1268,19 @@ TEST_F(FileUtilTest, CopyDirectoryExists) {
dir_name_to.Append(FILE_PATH_LITERAL("Subdir"));
// Create the destination directory.
- file_util::CreateDirectory(dir_name_to);
- ASSERT_TRUE(base::PathExists(dir_name_to));
+ CreateDirectory(dir_name_to);
+ ASSERT_TRUE(PathExists(dir_name_to));
- EXPECT_TRUE(base::CopyDirectory(dir_name_from, dir_name_to, false));
+ EXPECT_TRUE(CopyDirectory(dir_name_from, dir_name_to, false));
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(dir_name_from));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(subdir_name_from));
- EXPECT_TRUE(base::PathExists(file_name2_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
- EXPECT_FALSE(base::PathExists(subdir_name_to));
+ EXPECT_TRUE(PathExists(dir_name_from));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(subdir_name_from));
+ EXPECT_TRUE(PathExists(file_name2_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(subdir_name_to));
}
TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToNew) {
@@ -1402,17 +1288,17 @@ TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToNew) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination name
FilePath file_name_to = temp_dir_.path().Append(
FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
- ASSERT_FALSE(base::PathExists(file_name_to));
+ ASSERT_FALSE(PathExists(file_name_to));
- EXPECT_TRUE(base::CopyDirectory(file_name_from, file_name_to, true));
+ EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true));
// Check the has been copied
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExisting) {
@@ -1420,18 +1306,18 @@ TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExisting) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination name
FilePath file_name_to = temp_dir_.path().Append(
FILE_PATH_LITERAL("Copy_Test_File_Destination.txt"));
CreateTextFile(file_name_to, L"Old file content");
- ASSERT_TRUE(base::PathExists(file_name_to));
+ ASSERT_TRUE(PathExists(file_name_to));
- EXPECT_TRUE(base::CopyDirectory(file_name_from, file_name_to, true));
+ EXPECT_TRUE(CopyDirectory(file_name_from, file_name_to, true));
// Check the has been copied
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
EXPECT_TRUE(L"Gooooooooooooooooooooogle" == ReadTextFile(file_name_to));
}
@@ -1440,34 +1326,34 @@ TEST_F(FileUtilTest, CopyFileWithCopyDirectoryRecursiveToExistingDirectory) {
FilePath file_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// The destination
FilePath dir_name_to =
temp_dir_.path().Append(FILE_PATH_LITERAL("Destination"));
- file_util::CreateDirectory(dir_name_to);
- ASSERT_TRUE(base::PathExists(dir_name_to));
+ CreateDirectory(dir_name_to);
+ ASSERT_TRUE(PathExists(dir_name_to));
FilePath file_name_to =
dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
- EXPECT_TRUE(base::CopyDirectory(file_name_from, dir_name_to, true));
+ EXPECT_TRUE(CopyDirectory(file_name_from, dir_name_to, true));
// Check the has been copied
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) {
// Create a directory.
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory.
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Copy the directory recursively.
FilePath dir_name_to =
@@ -1484,51 +1370,51 @@ TEST_F(FileUtilTest, CopyDirectoryWithTrailingSeparators) {
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir///"));
#endif
- EXPECT_TRUE(base::CopyDirectory(from_path, dir_name_to, true));
+ EXPECT_TRUE(CopyDirectory(from_path, dir_name_to, true));
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(dir_name_from));
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_TRUE(PathExists(dir_name_from));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, CopyFile) {
// Create a directory
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("Copy_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt"));
const std::wstring file_contents(L"Gooooooooooooooooooooogle");
CreateTextFile(file_name_from, file_contents);
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Copy the file.
FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt"));
- ASSERT_TRUE(base::CopyFile(file_name_from, dest_file));
+ ASSERT_TRUE(CopyFile(file_name_from, dest_file));
// Copy the file to another location using '..' in the path.
FilePath dest_file2(dir_name_from);
dest_file2 = dest_file2.AppendASCII("..");
dest_file2 = dest_file2.AppendASCII("DestFile.txt");
- ASSERT_FALSE(base::CopyFile(file_name_from, dest_file2));
- ASSERT_TRUE(base::internal::CopyFileUnsafe(file_name_from, dest_file2));
+ ASSERT_FALSE(CopyFile(file_name_from, dest_file2));
+ ASSERT_TRUE(internal::CopyFileUnsafe(file_name_from, dest_file2));
FilePath dest_file2_test(dir_name_from);
dest_file2_test = dest_file2_test.DirName();
dest_file2_test = dest_file2_test.AppendASCII("DestFile.txt");
// Check everything has been copied.
- EXPECT_TRUE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(dest_file));
+ EXPECT_TRUE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(dest_file));
const std::wstring read_contents = ReadTextFile(dest_file);
EXPECT_EQ(file_contents, read_contents);
- EXPECT_TRUE(base::PathExists(dest_file2_test));
- EXPECT_TRUE(base::PathExists(dest_file2));
+ EXPECT_TRUE(PathExists(dest_file2_test));
+ EXPECT_TRUE(PathExists(dest_file2));
}
// file_util winds up using autoreleased objects on the Mac, so this needs
@@ -1537,9 +1423,9 @@ typedef PlatformTest ReadOnlyFileUtilTest;
TEST_F(ReadOnlyFileUtilTest, ContentsEqual) {
FilePath data_dir;
- ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir));
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
data_dir = data_dir.AppendASCII("file_util");
- ASSERT_TRUE(base::PathExists(data_dir));
+ ASSERT_TRUE(PathExists(data_dir));
FilePath original_file =
data_dir.Append(FILE_PATH_LITERAL("original.txt"));
@@ -1583,9 +1469,9 @@ TEST_F(ReadOnlyFileUtilTest, ContentsEqual) {
TEST_F(ReadOnlyFileUtilTest, TextContentsEqual) {
FilePath data_dir;
- ASSERT_TRUE(PathService::Get(base::DIR_TEST_DATA, &data_dir));
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
data_dir = data_dir.AppendASCII("file_util");
- ASSERT_TRUE(base::PathExists(data_dir));
+ ASSERT_TRUE(PathExists(data_dir));
FilePath original_file =
data_dir.Append(FILE_PATH_LITERAL("original.txt"));
@@ -1632,14 +1518,14 @@ TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) {
// Create a directory
FilePath dir_name_from =
temp_dir_.path().Append(FILE_PATH_LITERAL("CopyAndDelete_From_Subdir"));
- file_util::CreateDirectory(dir_name_from);
- ASSERT_TRUE(base::PathExists(dir_name_from));
+ CreateDirectory(dir_name_from);
+ ASSERT_TRUE(PathExists(dir_name_from));
// Create a file under the directory
FilePath file_name_from =
dir_name_from.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
- ASSERT_TRUE(base::PathExists(file_name_from));
+ ASSERT_TRUE(PathExists(file_name_from));
// Move the directory by using CopyAndDeleteDirectory
FilePath dir_name_to = temp_dir_.path().Append(
@@ -1647,16 +1533,16 @@ TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) {
FilePath file_name_to =
dir_name_to.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt"));
- ASSERT_FALSE(base::PathExists(dir_name_to));
+ ASSERT_FALSE(PathExists(dir_name_to));
- EXPECT_TRUE(base::internal::CopyAndDeleteDirectory(dir_name_from,
+ EXPECT_TRUE(internal::CopyAndDeleteDirectory(dir_name_from,
dir_name_to));
// Check everything has been moved.
- EXPECT_FALSE(base::PathExists(dir_name_from));
- EXPECT_FALSE(base::PathExists(file_name_from));
- EXPECT_TRUE(base::PathExists(dir_name_to));
- EXPECT_TRUE(base::PathExists(file_name_to));
+ EXPECT_FALSE(PathExists(dir_name_from));
+ EXPECT_FALSE(PathExists(file_name_from));
+ EXPECT_TRUE(PathExists(dir_name_to));
+ EXPECT_TRUE(PathExists(file_name_to));
}
TEST_F(FileUtilTest, GetTempDirTest) {
@@ -1673,7 +1559,7 @@ TEST_F(FileUtilTest, GetTempDirTest) {
for (unsigned int i = 0; i < arraysize(kTmpValues); ++i) {
FilePath path;
::_tputenv_s(kTmpKey, kTmpValues[i]);
- file_util::GetTempDir(&path);
+ GetTempDir(&path);
EXPECT_TRUE(path.IsAbsolute()) << "$TMP=" << kTmpValues[i] <<
" result=" << path.value();
}
@@ -1691,14 +1577,14 @@ TEST_F(FileUtilTest, GetTempDirTest) {
TEST_F(FileUtilTest, CreateTemporaryFileTest) {
FilePath temp_files[3];
for (int i = 0; i < 3; i++) {
- ASSERT_TRUE(file_util::CreateTemporaryFile(&(temp_files[i])));
- EXPECT_TRUE(base::PathExists(temp_files[i]));
+ ASSERT_TRUE(CreateTemporaryFile(&(temp_files[i])));
+ EXPECT_TRUE(PathExists(temp_files[i]));
EXPECT_FALSE(DirectoryExists(temp_files[i]));
}
for (int i = 0; i < 3; i++)
EXPECT_FALSE(temp_files[i] == temp_files[(i+1)%3]);
for (int i = 0; i < 3; i++)
- EXPECT_TRUE(base::DeleteFile(temp_files[i], false));
+ EXPECT_TRUE(DeleteFile(temp_files[i], false));
}
TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
@@ -1708,9 +1594,9 @@ TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
// Create; make sure they are open and exist.
for (i = 0; i < 3; ++i) {
- fps[i] = file_util::CreateAndOpenTemporaryFile(&(names[i]));
+ fps[i] = CreateAndOpenTemporaryFile(&(names[i]));
ASSERT_TRUE(fps[i]);
- EXPECT_TRUE(base::PathExists(names[i]));
+ EXPECT_TRUE(PathExists(names[i]));
}
// Make sure all names are unique.
@@ -1720,33 +1606,32 @@ TEST_F(FileUtilTest, CreateAndOpenTemporaryFileTest) {
// Close and delete.
for (i = 0; i < 3; ++i) {
- EXPECT_TRUE(file_util::CloseFile(fps[i]));
- EXPECT_TRUE(base::DeleteFile(names[i], false));
+ EXPECT_TRUE(CloseFile(fps[i]));
+ EXPECT_TRUE(DeleteFile(names[i], false));
}
}
TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
FilePath temp_dir;
- ASSERT_TRUE(file_util::CreateNewTempDirectory(FilePath::StringType(),
- &temp_dir));
- EXPECT_TRUE(base::PathExists(temp_dir));
- EXPECT_TRUE(base::DeleteFile(temp_dir, false));
+ ASSERT_TRUE(CreateNewTempDirectory(FilePath::StringType(), &temp_dir));
+ EXPECT_TRUE(PathExists(temp_dir));
+ EXPECT_TRUE(DeleteFile(temp_dir, false));
}
TEST_F(FileUtilTest, CreateNewTemporaryDirInDirTest) {
FilePath new_dir;
- ASSERT_TRUE(file_util::CreateTemporaryDirInDir(
+ ASSERT_TRUE(CreateTemporaryDirInDir(
temp_dir_.path(),
FILE_PATH_LITERAL("CreateNewTemporaryDirInDirTest"),
&new_dir));
- EXPECT_TRUE(base::PathExists(new_dir));
+ EXPECT_TRUE(PathExists(new_dir));
EXPECT_TRUE(temp_dir_.path().IsParent(new_dir));
- EXPECT_TRUE(base::DeleteFile(new_dir, false));
+ EXPECT_TRUE(DeleteFile(new_dir, false));
}
TEST_F(FileUtilTest, GetShmemTempDirTest) {
FilePath dir;
- EXPECT_TRUE(file_util::GetShmemTempDir(&dir, false));
+ EXPECT_TRUE(GetShmemTempDir(false, &dir));
EXPECT_TRUE(DirectoryExists(dir));
}
@@ -1761,22 +1646,22 @@ TEST_F(FileUtilTest, CreateDirectoryTest) {
test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/"));
#endif
- EXPECT_FALSE(base::PathExists(test_path));
- EXPECT_TRUE(file_util::CreateDirectory(test_path));
- EXPECT_TRUE(base::PathExists(test_path));
+ EXPECT_FALSE(PathExists(test_path));
+ EXPECT_TRUE(CreateDirectory(test_path));
+ EXPECT_TRUE(PathExists(test_path));
// CreateDirectory returns true if the DirectoryExists returns true.
- EXPECT_TRUE(file_util::CreateDirectory(test_path));
+ EXPECT_TRUE(CreateDirectory(test_path));
// Doesn't work to create it on top of a non-dir
test_path = test_path.Append(FILE_PATH_LITERAL("foobar.txt"));
- EXPECT_FALSE(base::PathExists(test_path));
+ EXPECT_FALSE(PathExists(test_path));
CreateTextFile(test_path, L"test file");
- EXPECT_TRUE(base::PathExists(test_path));
- EXPECT_FALSE(file_util::CreateDirectory(test_path));
+ EXPECT_TRUE(PathExists(test_path));
+ EXPECT_FALSE(CreateDirectory(test_path));
- EXPECT_TRUE(base::DeleteFile(test_root, true));
- EXPECT_FALSE(base::PathExists(test_root));
- EXPECT_FALSE(base::PathExists(test_path));
+ EXPECT_TRUE(DeleteFile(test_root, true));
+ EXPECT_FALSE(PathExists(test_root));
+ EXPECT_FALSE(PathExists(test_path));
// Verify assumptions made by the Windows implementation:
// 1. The current directory always exists.
@@ -1790,16 +1675,16 @@ TEST_F(FileUtilTest, CreateDirectoryTest) {
// Given these assumptions hold, it should be safe to
// test that "creating" these directories succeeds.
- EXPECT_TRUE(file_util::CreateDirectory(
+ EXPECT_TRUE(CreateDirectory(
FilePath(FilePath::kCurrentDirectory)));
- EXPECT_TRUE(file_util::CreateDirectory(top_level));
+ EXPECT_TRUE(CreateDirectory(top_level));
#if defined(OS_WIN)
FilePath invalid_drive(FILE_PATH_LITERAL("o:\\"));
FilePath invalid_path =
invalid_drive.Append(FILE_PATH_LITERAL("some\\inaccessible\\dir"));
- if (!base::PathExists(invalid_drive)) {
- EXPECT_FALSE(file_util::CreateDirectory(invalid_path));
+ if (!PathExists(invalid_drive)) {
+ EXPECT_FALSE(CreateDirectory(invalid_path));
}
#endif
}
@@ -1808,20 +1693,20 @@ TEST_F(FileUtilTest, DetectDirectoryTest) {
// Check a directory
FilePath test_root =
temp_dir_.path().Append(FILE_PATH_LITERAL("detect_directory_test"));
- EXPECT_FALSE(base::PathExists(test_root));
- EXPECT_TRUE(file_util::CreateDirectory(test_root));
- EXPECT_TRUE(base::PathExists(test_root));
+ EXPECT_FALSE(PathExists(test_root));
+ EXPECT_TRUE(CreateDirectory(test_root));
+ EXPECT_TRUE(PathExists(test_root));
EXPECT_TRUE(DirectoryExists(test_root));
// Check a file
FilePath test_path =
test_root.Append(FILE_PATH_LITERAL("foobar.txt"));
- EXPECT_FALSE(base::PathExists(test_path));
+ EXPECT_FALSE(PathExists(test_path));
CreateTextFile(test_path, L"test file");
- EXPECT_TRUE(base::PathExists(test_path));
+ EXPECT_TRUE(PathExists(test_path));
EXPECT_FALSE(DirectoryExists(test_path));
- EXPECT_TRUE(base::DeleteFile(test_path, false));
+ EXPECT_TRUE(DeleteFile(test_path, false));
- EXPECT_TRUE(base::DeleteFile(test_root, true));
+ EXPECT_TRUE(DeleteFile(test_root, true));
}
TEST_F(FileUtilTest, FileEnumeratorTest) {
@@ -1839,11 +1724,11 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
// create the directories
FilePath dir1 = temp_dir_.path().Append(FPL("dir1"));
- EXPECT_TRUE(file_util::CreateDirectory(dir1));
+ EXPECT_TRUE(CreateDirectory(dir1));
FilePath dir2 = temp_dir_.path().Append(FPL("dir2"));
- EXPECT_TRUE(file_util::CreateDirectory(dir2));
+ EXPECT_TRUE(CreateDirectory(dir2));
FilePath dir2inner = dir2.Append(FPL("inner"));
- EXPECT_TRUE(file_util::CreateDirectory(dir2inner));
+ EXPECT_TRUE(CreateDirectory(dir2inner));
// create the files
FilePath dir2file = dir2.Append(FPL("dir2file.txt"));
@@ -1929,7 +1814,7 @@ TEST_F(FileUtilTest, FileEnumeratorTest) {
ReparsePoint reparse_point(dir1, dir2);
EXPECT_TRUE(reparse_point.IsValid());
- if ((base::win::GetVersion() >= base::win::VERSION_VISTA)) {
+ if ((win::GetVersion() >= win::VERSION_VISTA)) {
// There can be a delay for the enumeration code to see the change on
// the file system so skip this test for XP.
// Enumerate the reparse point.
@@ -1977,16 +1862,16 @@ TEST_F(FileUtilTest, AppendToFile) {
temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
// Create a fresh, empty copy of this directory.
- if (base::PathExists(data_dir)) {
- ASSERT_TRUE(base::DeleteFile(data_dir, true));
+ if (PathExists(data_dir)) {
+ ASSERT_TRUE(DeleteFile(data_dir, true));
}
- ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+ ASSERT_TRUE(CreateDirectory(data_dir));
// Create a fresh, empty copy of this directory.
- if (base::PathExists(data_dir)) {
- ASSERT_TRUE(base::DeleteFile(data_dir, true));
+ if (PathExists(data_dir)) {
+ ASSERT_TRUE(DeleteFile(data_dir, true));
}
- ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+ ASSERT_TRUE(CreateDirectory(data_dir));
FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
std::string data("hello");
@@ -2005,30 +1890,30 @@ TEST_F(FileUtilTest, TouchFile) {
temp_dir_.path().Append(FILE_PATH_LITERAL("FilePathTest"));
// Create a fresh, empty copy of this directory.
- if (base::PathExists(data_dir)) {
- ASSERT_TRUE(base::DeleteFile(data_dir, true));
+ if (PathExists(data_dir)) {
+ ASSERT_TRUE(DeleteFile(data_dir, true));
}
- ASSERT_TRUE(file_util::CreateDirectory(data_dir));
+ ASSERT_TRUE(CreateDirectory(data_dir));
FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt")));
std::string data("hello");
ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length()));
- base::Time access_time;
+ Time access_time;
// This timestamp is divisible by one day (in local timezone),
// to make it work on FAT too.
- ASSERT_TRUE(base::Time::FromString("Wed, 16 Nov 1994, 00:00:00",
+ ASSERT_TRUE(Time::FromString("Wed, 16 Nov 1994, 00:00:00",
&access_time));
- base::Time modification_time;
+ Time modification_time;
// Note that this timestamp is divisible by two (seconds) - FAT stores
// modification times with 2s resolution.
- ASSERT_TRUE(base::Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT",
+ ASSERT_TRUE(Time::FromString("Tue, 15 Nov 1994, 12:45:26 GMT",
&modification_time));
- ASSERT_TRUE(file_util::TouchFile(foobar, access_time, modification_time));
- base::PlatformFileInfo file_info;
- ASSERT_TRUE(file_util::GetFileInfo(foobar, &file_info));
+ ASSERT_TRUE(TouchFile(foobar, access_time, modification_time));
+ PlatformFileInfo 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(),
@@ -2038,17 +1923,17 @@ TEST_F(FileUtilTest, TouchFile) {
TEST_F(FileUtilTest, IsDirectoryEmpty) {
FilePath empty_dir = temp_dir_.path().Append(FILE_PATH_LITERAL("EmptyDir"));
- ASSERT_FALSE(base::PathExists(empty_dir));
+ ASSERT_FALSE(PathExists(empty_dir));
- ASSERT_TRUE(file_util::CreateDirectory(empty_dir));
+ ASSERT_TRUE(CreateDirectory(empty_dir));
- EXPECT_TRUE(file_util::IsDirectoryEmpty(empty_dir));
+ EXPECT_TRUE(IsDirectoryEmpty(empty_dir));
FilePath foo(empty_dir.Append(FILE_PATH_LITERAL("foo.txt")));
std::string bar("baz");
ASSERT_TRUE(file_util::WriteFile(foo, bar.c_str(), bar.length()));
- EXPECT_FALSE(file_util::IsDirectoryEmpty(empty_dir));
+ EXPECT_FALSE(IsDirectoryEmpty(empty_dir));
}
#if defined(OS_POSIX)
@@ -2072,10 +1957,10 @@ class VerifyPathControlledByUserTest : public FileUtilTest {
// |-> text_file_
base_dir_ = temp_dir_.path().AppendASCII("base_dir");
- ASSERT_TRUE(file_util::CreateDirectory(base_dir_));
+ ASSERT_TRUE(CreateDirectory(base_dir_));
sub_dir_ = base_dir_.AppendASCII("sub_dir");
- ASSERT_TRUE(file_util::CreateDirectory(sub_dir_));
+ ASSERT_TRUE(CreateDirectory(sub_dir_));
text_file_ = sub_dir_.AppendASCII("file.txt");
CreateTextFile(text_file_, L"This text file has some text in it.");
@@ -2096,11 +1981,9 @@ class VerifyPathControlledByUserTest : public FileUtilTest {
// Users and group can read, write, traverse
int enabled_permissions =
- file_util::FILE_PERMISSION_USER_MASK |
- file_util::FILE_PERMISSION_GROUP_MASK;
+ FILE_PERMISSION_USER_MASK | FILE_PERMISSION_GROUP_MASK;
// Other users can't read, write, traverse
- int disabled_permissions =
- file_util::FILE_PERMISSION_OTHERS_MASK;
+ int disabled_permissions = FILE_PERMISSION_OTHERS_MASK;
ASSERT_NO_FATAL_FAILURE(
ChangePosixFilePermissions(
@@ -2150,7 +2033,7 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) {
// Symlink to the file at the end of the path.
FilePath file_link = base_dir_.AppendASCII("file_link");
- ASSERT_TRUE(file_util::CreateSymbolicLink(text_file_, file_link))
+ ASSERT_TRUE(CreateSymbolicLink(text_file_, file_link))
<< "Failed to create symlink.";
EXPECT_FALSE(
@@ -2162,11 +2045,11 @@ TEST_F(VerifyPathControlledByUserTest, Symlinks) {
// Symlink from one directory to another within the path.
FilePath link_to_sub_dir = base_dir_.AppendASCII("link_to_sub_dir");
- ASSERT_TRUE(file_util::CreateSymbolicLink(sub_dir_, link_to_sub_dir))
+ ASSERT_TRUE(CreateSymbolicLink(sub_dir_, link_to_sub_dir))
<< "Failed to create symlink.";
FilePath file_path_with_link = link_to_sub_dir.AppendASCII("file.txt");
- ASSERT_TRUE(base::PathExists(file_path_with_link));
+ ASSERT_TRUE(PathExists(file_path_with_link));
EXPECT_FALSE(
file_util::VerifyPathControlledByUser(
@@ -2421,6 +2304,53 @@ TEST_F(VerifyPathControlledByUserTest, WriteBitChecks) {
sub_dir_, text_file_, uid_, ok_gids_));
}
+#if defined(OS_ANDROID)
+TEST_F(FileUtilTest, ValidContentUriTest) {
+ // Get the test image path.
+ FilePath data_dir;
+ ASSERT_TRUE(PathService::Get(DIR_TEST_DATA, &data_dir));
+ data_dir = data_dir.AppendASCII("file_util");
+ ASSERT_TRUE(PathExists(data_dir));
+ FilePath image_file = data_dir.Append(FILE_PATH_LITERAL("red.png"));
+ int64 image_size;
+ GetFileSize(image_file, &image_size);
+ EXPECT_LT(0, image_size);
+
+ // Insert the image into MediaStore. MediaStore will do some conversions, and
+ // return the content URI.
+ FilePath path = file_util::InsertImageIntoMediaStore(image_file);
+ EXPECT_TRUE(path.IsContentUri());
+ EXPECT_TRUE(PathExists(path));
+ // The file size may not equal to the input image as MediaStore may convert
+ // the image.
+ int64 content_uri_size;
+ GetFileSize(path, &content_uri_size);
+ EXPECT_EQ(image_size, content_uri_size);
+
+ // 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));
+ delete[] buffer;
+}
+
+TEST_F(FileUtilTest, NonExistentContentUriTest) {
+ FilePath path("content://foo.bar");
+ EXPECT_TRUE(path.IsContentUri());
+ EXPECT_FALSE(PathExists(path));
+ // Size should be smaller than 0.
+ int64 size;
+ EXPECT_FALSE(GetFileSize(path, &size));
+
+ // We should not be able to read the file.
+ int fd = OpenContentUriForRead(path);
+ EXPECT_EQ(-1, fd);
+}
+#endif
+
#endif // defined(OS_POSIX)
} // namespace
+
+} // namespace base
diff --git a/chromium/base/file_util_win.cc b/chromium/base/file_util_win.cc
index 39317a3a932..44c1205aa65 100644
--- a/chromium/base/file_util_win.cc
+++ b/chromium/base/file_util_win.cc
@@ -92,7 +92,7 @@ bool DeleteFile(const FilePath& path, bool 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;
- if (file_util::GetFileInfo(path, &file_info) && file_info.is_directory)
+ if (GetFileInfo(path, &file_info) && file_info.is_directory)
return RemoveDirectory(path.value().c_str()) != 0;
// Otherwise, it's a file, wildcard or non-existant. Try DeleteFile first
@@ -107,8 +107,6 @@ bool DeleteFile(const FilePath& path, bool recursive) {
// into the rest of the buffer.
wchar_t double_terminated_path[MAX_PATH + 1] = {0};
#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
- if (g_bug108724_debug)
- LOG(WARNING) << "copying ";
wcscpy(double_terminated_path, path.value().c_str());
SHFILEOPSTRUCT file_operation = {0};
@@ -117,11 +115,7 @@ bool DeleteFile(const FilePath& path, bool recursive) {
file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
if (!recursive)
file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
- if (g_bug108724_debug)
- LOG(WARNING) << "Performing shell operation";
int err = SHFileOperation(&file_operation);
- if (g_bug108724_debug)
- LOG(WARNING) << "Done: " << err;
// Since we're passing flags to the operation telling it to be silent,
// it's possible for the operation to be aborted/cancelled without err
@@ -184,7 +178,7 @@ bool CopyDirectory(const FilePath& from_path, const FilePath& 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)
- file_util::CreateDirectory(to_path);
+ CreateDirectory(to_path);
else
ShellCopy(from_path, to_path, false);
}
@@ -219,19 +213,7 @@ bool DirectoryExists(const FilePath& path) {
return false;
}
-} // namespace base
-
-// -----------------------------------------------------------------------------
-
-namespace file_util {
-
-using base::DirectoryExists;
-using base::FilePath;
-using base::kFileShareAll;
-
bool GetTempDir(FilePath* path) {
- base::ThreadRestrictions::AssertIOAllowed();
-
wchar_t temp_path[MAX_PATH + 1];
DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
if (path_len >= MAX_PATH || path_len <= 0)
@@ -243,12 +225,12 @@ bool GetTempDir(FilePath* path) {
return true;
}
-bool GetShmemTempDir(FilePath* path, bool executable) {
+bool GetShmemTempDir(bool executable, FilePath* path) {
return GetTempDir(path);
}
bool CreateTemporaryFile(FilePath* path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
FilePath temp_file;
@@ -264,7 +246,7 @@ bool CreateTemporaryFile(FilePath* path) {
}
FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
return CreateAndOpenTemporaryFile(path);
}
@@ -273,7 +255,7 @@ FILE* CreateAndOpenTemporaryShmemFile(FilePath* path, bool executable) {
// TODO(jrg): is there equivalent call to use on Windows instead of
// going 2-step?
FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
if (!CreateTemporaryFileInDir(dir, path)) {
return NULL;
}
@@ -283,14 +265,14 @@ FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) {
return OpenFile(*path, "wb+");
}
-bool CreateTemporaryFileInDir(const FilePath& dir,
- FilePath* temp_file) {
- base::ThreadRestrictions::AssertIOAllowed();
+bool CreateTemporaryFileInDir(const FilePath& dir, FilePath* temp_file) {
+ ThreadRestrictions::AssertIOAllowed();
wchar_t temp_name[MAX_PATH + 1];
if (!GetTempFileName(dir.value().c_str(), L"", 0, temp_name)) {
- DPLOG(WARNING) << "Failed to get temporary file name in " << dir.value();
+ DPLOG(WARNING) << "Failed to get temporary file name in "
+ << UTF16ToUTF8(dir.value());
return false;
}
@@ -311,7 +293,7 @@ bool CreateTemporaryFileInDir(const FilePath& dir,
bool CreateTemporaryDirInDir(const FilePath& base_dir,
const FilePath::StringType& prefix,
FilePath* new_dir) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
FilePath path_to_create;
@@ -320,9 +302,9 @@ bool CreateTemporaryDirInDir(const FilePath& base_dir,
// the one exists, keep trying another path name until we reach some limit.
string16 new_dir_name;
new_dir_name.assign(prefix);
- new_dir_name.append(base::IntToString16(::base::GetCurrentProcId()));
+ new_dir_name.append(IntToString16(GetCurrentProcId()));
new_dir_name.push_back('_');
- new_dir_name.append(base::IntToString16(base::RandInt(0, kint16max)));
+ new_dir_name.append(IntToString16(RandInt(0, kint16max)));
path_to_create = base_dir.Append(new_dir_name);
if (::CreateDirectory(path_to_create.value().c_str(), NULL)) {
@@ -336,7 +318,7 @@ bool CreateTemporaryDirInDir(const FilePath& base_dir,
bool CreateNewTempDirectory(const FilePath::StringType& prefix,
FilePath* new_temp_path) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
FilePath system_temp_dir;
if (!GetTempDir(&system_temp_dir))
@@ -346,8 +328,8 @@ bool CreateNewTempDirectory(const FilePath::StringType& prefix,
}
bool CreateDirectoryAndGetError(const FilePath& full_path,
- base::PlatformFileError* error) {
- base::ThreadRestrictions::AssertIOAllowed();
+ PlatformFileError* error) {
+ ThreadRestrictions::AssertIOAllowed();
// If the path exists, we've succeeded if it's a directory, failed otherwise.
const wchar_t* full_path_str = full_path.value().c_str();
@@ -361,7 +343,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
DLOG(WARNING) << "CreateDirectory(" << full_path_str << "), "
<< "conflicts with existing file.";
if (error) {
- *error = base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
+ *error = PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
}
return false;
}
@@ -374,14 +356,14 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
FilePath parent_path(full_path.DirName());
if (parent_path.value() == full_path.value()) {
if (error) {
- *error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
+ *error = PLATFORM_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 != base::PLATFORM_FILE_OK);
+ DCHECK(*error != PLATFORM_FILE_OK);
}
return false;
}
@@ -396,7 +378,7 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
return true;
} else {
if (error)
- *error = base::LastErrorToPlatformFileError(error_code);
+ *error = LastErrorToPlatformFileError(error_code);
DLOG(WARNING) << "Failed to create directory " << full_path_str
<< ", last error is " << error_code << ".";
return false;
@@ -406,14 +388,125 @@ bool CreateDirectoryAndGetError(const FilePath& full_path,
}
}
+bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
+ ThreadRestrictions::AssertIOAllowed();
+ FilePath mapped_file;
+ if (!NormalizeToNativeFilePath(path, &mapped_file))
+ return false;
+ // NormalizeToNativeFilePath() will return a path that starts with
+ // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
+ // will find a drive letter which maps to the path's device, so
+ // that we return a path starting with a drive letter.
+ return DevicePathToDriveLetterPath(mapped_file, real_path);
+}
+
+bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
+ FilePath* out_drive_letter_path) {
+ ThreadRestrictions::AssertIOAllowed();
+
+ // Get the mapping of drive letters to device paths.
+ const int kDriveMappingSize = 1024;
+ wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
+ if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
+ DLOG(ERROR) << "Failed to get drive mapping.";
+ return false;
+ }
+
+ // The drive mapping is a sequence of null terminated strings.
+ // The last string is empty.
+ wchar_t* drive_map_ptr = drive_mapping;
+ wchar_t device_path_as_string[MAX_PATH];
+ wchar_t drive[] = L" :";
+
+ // For each string in the drive mapping, get the junction that links
+ // to it. If that junction is a prefix of |device_path|, then we
+ // know that |drive| is the real path prefix.
+ while (*drive_map_ptr) {
+ drive[0] = drive_map_ptr[0]; // Copy the drive letter.
+
+ if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
+ FilePath device_path(device_path_as_string);
+ if (device_path == nt_device_path ||
+ device_path.IsParent(nt_device_path)) {
+ *out_drive_letter_path = FilePath(drive +
+ nt_device_path.value().substr(wcslen(device_path_as_string)));
+ return true;
+ }
+ }
+ // Move to the next drive letter string, which starts one
+ // increment after the '\0' that terminates the current string.
+ while (*drive_map_ptr++);
+ }
+
+ // No drive matched. The path does not start with a device junction
+ // that is mounted as a drive letter. This means there is no drive
+ // letter path to the volume that holds |device_path|, so fail.
+ return false;
+}
+
+bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
+ ThreadRestrictions::AssertIOAllowed();
+ // In Vista, GetFinalPathNameByHandle() would give us the real path
+ // from a file handle. If we ever deprecate XP, consider changing the
+ // code below to a call to GetFinalPathNameByHandle(). The method this
+ // function uses is explained in the following msdn article:
+ // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
+ base::win::ScopedHandle file_handle(
+ ::CreateFile(path.value().c_str(),
+ GENERIC_READ,
+ kFileShareAll,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL));
+ if (!file_handle)
+ return false;
+
+ // Create a file mapping object. Can't easily use MemoryMappedFile, because
+ // we only map the first byte, and need direct access to the handle. You can
+ // not map an empty file, this call fails in that case.
+ base::win::ScopedHandle file_map_handle(
+ ::CreateFileMapping(file_handle.Get(),
+ NULL,
+ PAGE_READONLY,
+ 0,
+ 1, // Just one byte. No need to look at the data.
+ NULL));
+ if (!file_map_handle)
+ return false;
+
+ // Use a view of the file to get the path to the file.
+ void* file_view = MapViewOfFile(file_map_handle.Get(),
+ FILE_MAP_READ, 0, 0, 1);
+ if (!file_view)
+ return false;
+
+ // The expansion of |path| into a full path may make it longer.
+ // GetMappedFileName() will fail if the result is longer than MAX_PATH.
+ // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
+ // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
+ // not return kMaxPathLength. This would mean that only part of the
+ // path fit in |mapped_file_path|.
+ const int kMaxPathLength = MAX_PATH + 10;
+ wchar_t mapped_file_path[kMaxPathLength];
+ bool success = false;
+ HANDLE cp = GetCurrentProcess();
+ if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
+ *nt_path = FilePath(mapped_file_path);
+ success = true;
+ }
+ ::UnmapViewOfFile(file_view);
+ return success;
+}
+
// TODO(rkc): Work out if we want to handle NTFS junctions here or not, handle
// them if we do decide to.
bool IsLink(const FilePath& file_path) {
return false;
}
-bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
- base::ThreadRestrictions::AssertIOAllowed();
+bool GetFileInfo(const FilePath& file_path, PlatformFileInfo* results) {
+ ThreadRestrictions::AssertIOAllowed();
WIN32_FILE_ATTRIBUTE_DATA attr;
if (!GetFileAttributesEx(file_path.value().c_str(),
@@ -428,26 +521,21 @@ bool GetFileInfo(const FilePath& file_path, base::PlatformFileInfo* results) {
results->is_directory =
(attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- results->last_modified = base::Time::FromFileTime(attr.ftLastWriteTime);
- results->last_accessed = base::Time::FromFileTime(attr.ftLastAccessTime);
- results->creation_time = base::Time::FromFileTime(attr.ftCreationTime);
+ results->last_modified = Time::FromFileTime(attr.ftLastWriteTime);
+ results->last_accessed = Time::FromFileTime(attr.ftLastAccessTime);
+ results->creation_time = Time::FromFileTime(attr.ftCreationTime);
return true;
}
FILE* OpenFile(const FilePath& filename, const char* mode) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
std::wstring w_mode = ASCIIToWide(std::string(mode));
return _wfsopen(filename.value().c_str(), w_mode.c_str(), _SH_DENYNO);
}
-FILE* OpenFile(const std::string& filename, const char* mode) {
- base::ThreadRestrictions::AssertIOAllowed();
- return _fsopen(filename.c_str(), mode, _SH_DENYNO);
-}
-
int ReadFile(const FilePath& filename, char* data, int size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
@@ -465,6 +553,21 @@ int ReadFile(const FilePath& filename, char* data, int size) {
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);
+}
+
int WriteFile(const FilePath& filename, const char* data, int size) {
base::ThreadRestrictions::AssertIOAllowed();
base::win::ScopedHandle file(CreateFile(filename.value().c_str(),
@@ -553,117 +656,6 @@ bool SetCurrentDirectory(const FilePath& directory) {
return ret != 0;
}
-bool NormalizeFilePath(const FilePath& path, FilePath* real_path) {
- base::ThreadRestrictions::AssertIOAllowed();
- FilePath mapped_file;
- if (!NormalizeToNativeFilePath(path, &mapped_file))
- return false;
- // NormalizeToNativeFilePath() will return a path that starts with
- // "\Device\Harddisk...". Helper DevicePathToDriveLetterPath()
- // will find a drive letter which maps to the path's device, so
- // that we return a path starting with a drive letter.
- return DevicePathToDriveLetterPath(mapped_file, real_path);
-}
-
-bool DevicePathToDriveLetterPath(const FilePath& nt_device_path,
- FilePath* out_drive_letter_path) {
- base::ThreadRestrictions::AssertIOAllowed();
-
- // Get the mapping of drive letters to device paths.
- const int kDriveMappingSize = 1024;
- wchar_t drive_mapping[kDriveMappingSize] = {'\0'};
- if (!::GetLogicalDriveStrings(kDriveMappingSize - 1, drive_mapping)) {
- DLOG(ERROR) << "Failed to get drive mapping.";
- return false;
- }
-
- // The drive mapping is a sequence of null terminated strings.
- // The last string is empty.
- wchar_t* drive_map_ptr = drive_mapping;
- wchar_t device_path_as_string[MAX_PATH];
- wchar_t drive[] = L" :";
-
- // For each string in the drive mapping, get the junction that links
- // to it. If that junction is a prefix of |device_path|, then we
- // know that |drive| is the real path prefix.
- while (*drive_map_ptr) {
- drive[0] = drive_map_ptr[0]; // Copy the drive letter.
-
- if (QueryDosDevice(drive, device_path_as_string, MAX_PATH)) {
- FilePath device_path(device_path_as_string);
- if (device_path == nt_device_path ||
- device_path.IsParent(nt_device_path)) {
- *out_drive_letter_path = FilePath(drive +
- nt_device_path.value().substr(wcslen(device_path_as_string)));
- return true;
- }
- }
- // Move to the next drive letter string, which starts one
- // increment after the '\0' that terminates the current string.
- while (*drive_map_ptr++);
- }
-
- // No drive matched. The path does not start with a device junction
- // that is mounted as a drive letter. This means there is no drive
- // letter path to the volume that holds |device_path|, so fail.
- return false;
-}
-
-bool NormalizeToNativeFilePath(const FilePath& path, FilePath* nt_path) {
- base::ThreadRestrictions::AssertIOAllowed();
- // In Vista, GetFinalPathNameByHandle() would give us the real path
- // from a file handle. If we ever deprecate XP, consider changing the
- // code below to a call to GetFinalPathNameByHandle(). The method this
- // function uses is explained in the following msdn article:
- // http://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
- base::win::ScopedHandle file_handle(
- ::CreateFile(path.value().c_str(),
- GENERIC_READ,
- kFileShareAll,
- NULL,
- OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL,
- NULL));
- if (!file_handle)
- return false;
-
- // Create a file mapping object. Can't easily use MemoryMappedFile, because
- // we only map the first byte, and need direct access to the handle. You can
- // not map an empty file, this call fails in that case.
- base::win::ScopedHandle file_map_handle(
- ::CreateFileMapping(file_handle.Get(),
- NULL,
- PAGE_READONLY,
- 0,
- 1, // Just one byte. No need to look at the data.
- NULL));
- if (!file_map_handle)
- return false;
-
- // Use a view of the file to get the path to the file.
- void* file_view = MapViewOfFile(file_map_handle.Get(),
- FILE_MAP_READ, 0, 0, 1);
- if (!file_view)
- return false;
-
- // The expansion of |path| into a full path may make it longer.
- // GetMappedFileName() will fail if the result is longer than MAX_PATH.
- // Pad a bit to be safe. If kMaxPathLength is ever changed to be less
- // than MAX_PATH, it would be nessisary to test that GetMappedFileName()
- // not return kMaxPathLength. This would mean that only part of the
- // path fit in |mapped_file_path|.
- const int kMaxPathLength = MAX_PATH + 10;
- wchar_t mapped_file_path[kMaxPathLength];
- bool success = false;
- HANDLE cp = GetCurrentProcess();
- if (::GetMappedFileNameW(cp, file_view, mapped_file_path, kMaxPathLength)) {
- *nt_path = FilePath(mapped_file_path);
- success = true;
- }
- ::UnmapViewOfFile(file_view);
- return success;
-}
-
int GetMaximumPathComponentLength(const FilePath& path) {
base::ThreadRestrictions::AssertIOAllowed();
diff --git a/chromium/base/file_version_info.h b/chromium/base/file_version_info.h
index 59cd45df07d..e18ba1367f0 100644
--- a/chromium/base/file_version_info.h
+++ b/chromium/base/file_version_info.h
@@ -66,21 +66,21 @@ class FileVersionInfo {
// Accessors to the different version properties.
// Returns an empty string if the property is not found.
- virtual string16 company_name() = 0;
- virtual string16 company_short_name() = 0;
- virtual string16 product_name() = 0;
- virtual string16 product_short_name() = 0;
- virtual string16 internal_name() = 0;
- virtual string16 product_version() = 0;
- virtual string16 private_build() = 0;
- virtual string16 special_build() = 0;
- virtual string16 comments() = 0;
- virtual string16 original_filename() = 0;
- virtual string16 file_description() = 0;
- virtual string16 file_version() = 0;
- virtual string16 legal_copyright() = 0;
- virtual string16 legal_trademarks() = 0;
- virtual string16 last_change() = 0;
+ virtual base::string16 company_name() = 0;
+ virtual base::string16 company_short_name() = 0;
+ virtual base::string16 product_name() = 0;
+ virtual base::string16 product_short_name() = 0;
+ virtual base::string16 internal_name() = 0;
+ virtual base::string16 product_version() = 0;
+ virtual base::string16 private_build() = 0;
+ virtual base::string16 special_build() = 0;
+ virtual base::string16 comments() = 0;
+ virtual base::string16 original_filename() = 0;
+ virtual base::string16 file_description() = 0;
+ virtual base::string16 file_version() = 0;
+ virtual base::string16 legal_copyright() = 0;
+ virtual base::string16 legal_trademarks() = 0;
+ virtual base::string16 last_change() = 0;
virtual bool is_official_build() = 0;
};
diff --git a/chromium/base/files/dir_reader_linux.h b/chromium/base/files/dir_reader_linux.h
index 3e0721ec26c..cb0cbd38e56 100644
--- a/chromium/base/files/dir_reader_linux.h
+++ b/chromium/base/files/dir_reader_linux.h
@@ -37,7 +37,7 @@ class DirReaderLinux {
~DirReaderLinux() {
if (fd_ >= 0) {
- if (HANDLE_EINTR(close(fd_)))
+ if (IGNORE_EINTR(close(fd_)))
RAW_LOG(ERROR, "Failed to close directory handle");
}
}
diff --git a/chromium/base/files/file.cc b/chromium/base/files/file.cc
new file mode 100644
index 00000000000..4902f15a2ff
--- /dev/null
+++ b/chromium/base/files/file.cc
@@ -0,0 +1,64 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file.h"
+
+// TODO(rvargas): remove this (needed for kInvalidPlatformFileValue).
+#include "base/platform_file.h"
+
+namespace base {
+
+File::Info::Info()
+ : size(0),
+ is_directory(false),
+ is_symbolic_link(false) {
+}
+
+File::Info::~Info() {
+}
+
+File::File()
+ : file_(kInvalidPlatformFileValue),
+ error_(FILE_OK),
+ created_(false),
+ async_(false) {
+}
+
+#if !defined(OS_NACL)
+File::File(const FilePath& name, uint32 flags)
+ : file_(kInvalidPlatformFileValue),
+ error_(FILE_OK),
+ created_(false),
+ async_(false) {
+ if (name.ReferencesParent()) {
+ error_ = FILE_ERROR_ACCESS_DENIED;
+ return;
+ }
+ CreateBaseFileUnsafe(name, flags);
+}
+#endif
+
+File::File(RValue other)
+ : file_(other.object->TakePlatformFile()),
+ error_(other.object->error()),
+ created_(other.object->created()),
+ async_(other.object->async_) {
+}
+
+File::~File() {
+ Close();
+}
+
+File& File::operator=(RValue other) {
+ if (this != other.object) {
+ Close();
+ SetPlatformFile(other.object->TakePlatformFile());
+ error_ = other.object->error();
+ created_ = other.object->created();
+ async_ = other.object->async_;
+ }
+ return *this;
+}
+
+} // namespace base
diff --git a/chromium/base/files/file.h b/chromium/base/files/file.h
new file mode 100644
index 00000000000..d1e0e8ca587
--- /dev/null
+++ b/chromium/base/files/file.h
@@ -0,0 +1,280 @@
+// Copyright (c) 2012 The Chromium Authors. All 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_H_
+#define BASE_FILES_FILE_H_
+
+#include "build/build_config.h"
+#if defined(OS_WIN)
+#include <windows.h>
+#endif
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/files/file_path.h"
+#include "base/move.h"
+#include "base/time/time.h"
+
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
+namespace base {
+
+#if defined(OS_WIN)
+typedef HANDLE PlatformFile;
+#elif defined(OS_POSIX)
+typedef int PlatformFile;
+#endif
+
+
+// Thin wrapper around an OS-level file.
+// Note that this class does not provide any support for asynchronous IO, other
+// than the ability to create asynchronous handles on Windows.
+//
+// Note about const: this class does not attempt to determine if the underlying
+// file system object is affected by a particular method in order to consider
+// that method const or not. Only methods that deal with member variables in an
+// obvious non-modifying way are marked as const. Any method that forward calls
+// to the OS is not considered const, even if there is no apparent change to
+// member variables.
+class BASE_EXPORT File {
+ MOVE_ONLY_TYPE_FOR_CPP_03(File, RValue)
+
+ public:
+ // FLAG_(OPEN|CREATE).* are mutually exclusive. You should specify exactly one
+ // of the five (possibly combining with other flags) when opening or creating
+ // a file.
+ // FLAG_(WRITE|APPEND) are mutually exclusive. This is so that APPEND behavior
+ // will be consistent with O_APPEND on POSIX.
+ // FLAG_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file on
+ // creation on POSIX; for existing files, consider using Lock().
+ enum Flags {
+ FLAG_OPEN = 1 << 0, // Opens a file, only if it exists.
+ FLAG_CREATE = 1 << 1, // Creates a new file, only if it does not
+ // already exist.
+ FLAG_OPEN_ALWAYS = 1 << 2, // May create a new file.
+ FLAG_CREATE_ALWAYS = 1 << 3, // May overwrite an old file.
+ FLAG_OPEN_TRUNCATED = 1 << 4, // Opens a file and truncates it, only if it
+ // exists.
+ FLAG_READ = 1 << 5,
+ FLAG_WRITE = 1 << 6,
+ FLAG_APPEND = 1 << 7,
+ FLAG_EXCLUSIVE_READ = 1 << 8, // EXCLUSIVE is opposite of Windows SHARE.
+ FLAG_EXCLUSIVE_WRITE = 1 << 9,
+ FLAG_ASYNC = 1 << 10,
+ FLAG_TEMPORARY = 1 << 11, // Used on Windows only.
+ FLAG_HIDDEN = 1 << 12, // Used on Windows only.
+ FLAG_DELETE_ON_CLOSE = 1 << 13,
+ FLAG_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only.
+ FLAG_SHARE_DELETE = 1 << 15, // Used on Windows only.
+ FLAG_TERMINAL_DEVICE = 1 << 16, // Serial port flags.
+ FLAG_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only.
+ FLAG_EXECUTE = 1 << 18, // Used on Windows only.
+ };
+
+ // This enum has been recorded in multiple histograms. If the order of the
+ // fields needs to change, please ensure that those histograms are obsolete or
+ // have been moved to a different enum.
+ //
+ // FILE_ERROR_ACCESS_DENIED is returned when a call fails because of a
+ // filesystem restriction. FILE_ERROR_SECURITY is returned when a browser
+ // policy doesn't allow the operation to be executed.
+ enum Error {
+ FILE_OK = 0,
+ FILE_ERROR_FAILED = -1,
+ FILE_ERROR_IN_USE = -2,
+ FILE_ERROR_EXISTS = -3,
+ FILE_ERROR_NOT_FOUND = -4,
+ FILE_ERROR_ACCESS_DENIED = -5,
+ FILE_ERROR_TOO_MANY_OPENED = -6,
+ FILE_ERROR_NO_MEMORY = -7,
+ FILE_ERROR_NO_SPACE = -8,
+ FILE_ERROR_NOT_A_DIRECTORY = -9,
+ FILE_ERROR_INVALID_OPERATION = -10,
+ FILE_ERROR_SECURITY = -11,
+ FILE_ERROR_ABORT = -12,
+ FILE_ERROR_NOT_A_FILE = -13,
+ FILE_ERROR_NOT_EMPTY = -14,
+ FILE_ERROR_INVALID_URL = -15,
+ FILE_ERROR_IO = -16,
+ // Put new entries here and increment FILE_ERROR_MAX.
+ FILE_ERROR_MAX = -17
+ };
+
+ // This explicit mapping matches both FILE_ on Windows and SEEK_ on Linux.
+ enum Whence {
+ FROM_BEGIN = 0,
+ FROM_CURRENT = 1,
+ FROM_END = 2
+ };
+
+ // Used to hold information about a given file.
+ // If you add more fields to this structure (platform-specific fields are OK),
+ // make sure to update all functions that use it in file_util_{win|posix}.cc
+ // too, and the ParamTraits<base::PlatformFileInfo> implementation in
+ // chrome/common/common_param_traits.cc.
+ struct BASE_EXPORT Info {
+ Info();
+ ~Info();
+
+ // The size of the file in bytes. Undefined when is_directory is true.
+ int64 size;
+
+ // True if the file corresponds to a directory.
+ bool is_directory;
+
+ // True if the file corresponds to a symbolic link.
+ bool is_symbolic_link;
+
+ // The last modified time of a file.
+ base::Time last_modified;
+
+ // The last accessed time of a file.
+ base::Time last_accessed;
+
+ // The creation time of a file.
+ base::Time creation_time;
+ };
+
+ File();
+
+ // Creates or opens the given file. This will fail with 'access denied' if the
+ // |name| contains path traversal ('..') components.
+ File(const FilePath& name, uint32 flags);
+
+ // Takes ownership of |platform_file|.
+ explicit File(PlatformFile platform_file);
+
+ // Move constructor for C++03 move emulation of this type.
+ File(RValue other);
+
+ ~File();
+
+ // Move operator= for C++03 move emulation of this type.
+ File& operator=(RValue other);
+
+ // Creates or opens the given file, allowing paths with traversal ('..')
+ // components. Use only with extreme care.
+ void CreateBaseFileUnsafe(const FilePath& name, uint32 flags);
+
+ bool IsValid() const;
+
+ // Returns true if a new file was created (or an old one truncated to zero
+ // length to simulate a new file, which can happen with
+ // FLAG_CREATE_ALWAYS), and false otherwise.
+ bool created() const { return created_; }
+
+ // Returns the OS result of opening this file.
+ Error error() const { return error_; }
+
+ PlatformFile GetPlatformFile() const { return file_; }
+ PlatformFile TakePlatformFile();
+
+ // Destroying this object closes the file automatically.
+ void Close();
+
+ // Changes current position in the file to an |offset| relative to an origin
+ // defined by |whence|. Returns the resultant current position in the file
+ // (relative to the start) or -1 in case of error.
+ int64 Seek(Whence whence, int64 offset);
+
+ // Reads the given number of bytes (or until EOF is reached) starting with the
+ // given offset. Returns the number of bytes read, or -1 on error. Note that
+ // this function makes a best effort to read all data on all platforms, so it
+ // is not intended for stream oriented files but instead for cases when the
+ // normal expectation is that actually |size| bytes are read unless there is
+ // an error.
+ int Read(int64 offset, char* data, int size);
+
+ // Same as above but without seek.
+ int ReadAtCurrentPos(char* data, int size);
+
+ // Reads the given number of bytes (or until EOF is reached) starting with the
+ // given offset, but does not make any effort to read all data on all
+ // platforms. Returns the number of bytes read, or -1 on error.
+ int ReadNoBestEffort(int64 offset, char* data, int size);
+
+ // Same as above but without seek.
+ int ReadAtCurrentPosNoBestEffort(char* data, int size);
+
+ // Writes the given buffer into the file at the given offset, overwritting any
+ // data that was previously there. Returns the number of bytes written, or -1
+ // on error. Note that this function makes a best effort to write all data on
+ // all platforms.
+ // Ignores the offset and writes to the end of the file if the file was opened
+ // with FLAG_APPEND.
+ int Write(int64 offset, const char* data, int size);
+
+ // Save as above but without seek.
+ int WriteAtCurrentPos(const char* data, int size);
+
+ // Save as above but does not make any effort to write all data on all
+ // platforms. Returns the number of bytes written, or -1 on error.
+ int WriteAtCurrentPosNoBestEffort(const char* data, int size);
+
+ // 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);
+
+ // Flushes the buffers.
+ bool Flush();
+
+ // Updates the file times.
+ bool SetTimes(Time last_access_time, Time last_modified_time);
+
+ // Returns some basic information for the given file.
+ bool GetInfo(Info* info);
+
+ // Attempts to take an exclusive write lock on the file. Returns immediately
+ // (i.e. does not wait for another process to unlock the file). If the lock
+ // was obtained, the result will be FILE_OK. A lock only guarantees
+ // that other processes may not also take a lock on the same file with the
+ // same API - it may still be opened, renamed, unlinked, etc.
+ //
+ // Common semantics:
+ // * Locks are held by processes, but not inherited by child processes.
+ // * Locks are released by the OS on file close or process termination.
+ // * Locks are reliable only on local filesystems.
+ // * Duplicated file handles may also write to locked files.
+ // Windows-specific semantics:
+ // * Locks are mandatory for read/write APIs, advisory for mapping APIs.
+ // * Within a process, locking the same file (by the same or new handle)
+ // will fail.
+ // POSIX-specific semantics:
+ // * Locks are advisory only.
+ // * Within a process, locking the same file (by the same or new handle)
+ // will succeed.
+ // * Closing any descriptor on a given file releases the lock.
+ Error Lock();
+
+ // Unlock a file previously locked.
+ Error Unlock();
+
+#if defined(OS_WIN)
+ static Error OSErrorToFileError(DWORD last_error);
+#elif defined(OS_POSIX)
+ static Error OSErrorToFileError(int saved_errno);
+#endif
+
+ private:
+ void SetPlatformFile(PlatformFile file);
+
+#if defined(OS_WIN)
+ win::ScopedHandle file_;
+#elif defined(OS_POSIX)
+ PlatformFile file_;
+#endif
+
+ Error error_;
+ bool created_;
+ bool async_;
+};
+
+} // namespace base
+
+#endif // BASE_FILES_FILE_H_
diff --git a/chromium/base/files/file_enumerator.h b/chromium/base/files/file_enumerator.h
index ce9bd1fd265..38bb8337c70 100644
--- a/chromium/base/files/file_enumerator.h
+++ b/chromium/base/files/file_enumerator.h
@@ -53,6 +53,9 @@ class BASE_EXPORT FileEnumerator {
Time GetLastModifiedTime() const;
#if defined(OS_WIN)
+ // Note that the cAlternateFileName (used to hold the "short" 8.3 name)
+ // of the WIN32_FIND_DATA will be empty. Since we don't use short file
+ // names, we tell Windows to omit it which speeds up the query slightly.
const WIN32_FIND_DATA& find_data() const { return find_data_; }
#elif defined(OS_POSIX)
const struct stat& stat() const { return stat_; }
diff --git a/chromium/base/files/file_enumerator_win.cc b/chromium/base/files/file_enumerator_win.cc
index e47f5421a71..6da1667ed99 100644
--- a/chromium/base/files/file_enumerator_win.cc
+++ b/chromium/base/files/file_enumerator_win.cc
@@ -8,6 +8,7 @@
#include "base/logging.h"
#include "base/threading/thread_restrictions.h"
+#include "base/win/windows_version.h"
namespace base {
@@ -99,7 +100,18 @@ FilePath FileEnumerator::Next() {
else
src = src.Append(pattern_);
- find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
+ if (base::win::GetVersion() >= base::win::VERSION_WIN7) {
+ // Use a "large fetch" on newer Windows which should speed up large
+ // enumerations (we seldom abort in the middle).
+ find_handle_ = FindFirstFileEx(src.value().c_str(),
+ FindExInfoBasic, // Omit short name.
+ &find_data_,
+ FindExSearchNameMatch,
+ NULL,
+ FIND_FIRST_EX_LARGE_FETCH);
+ } else {
+ find_handle_ = FindFirstFile(src.value().c_str(), &find_data_);
+ }
has_find_data_ = true;
} else {
// Search for the next file/directory.
diff --git a/chromium/base/files/file_path.cc b/chromium/base/files/file_path.cc
index be346342937..3ea5856a0d5 100644
--- a/chromium/base/files/file_path.cc
+++ b/chromium/base/files/file_path.cc
@@ -107,18 +107,21 @@ bool AreAllSeparators(const StringType& input) {
// Find the position of the '.' that separates the extension from the rest
// of the file name. The position is relative to BaseName(), not value().
-// This allows a second extension component of up to 4 characters when the
-// rightmost extension component is a common double extension (gz, bz2, Z).
-// For example, foo.tar.gz or foo.tar.Z would have extension components of
-// '.tar.gz' and '.tar.Z' respectively. Returns npos if it can't find an
-// extension.
-StringType::size_type ExtensionSeparatorPosition(const StringType& path) {
+// Returns npos if it can't find an extension.
+StringType::size_type FinalExtensionSeparatorPosition(const StringType& path) {
// Special case "." and ".."
if (path == FilePath::kCurrentDirectory || path == FilePath::kParentDirectory)
return StringType::npos;
- const StringType::size_type last_dot =
- path.rfind(FilePath::kExtensionSeparator);
+ return path.rfind(FilePath::kExtensionSeparator);
+}
+
+// Same as above, but allow a second extension component of up to 4
+// characters when the rightmost extension component is a common double
+// extension (gz, bz2, Z). For example, foo.tar.gz or foo.tar.Z would have
+// extension components of '.tar.gz' and '.tar.Z' respectively.
+StringType::size_type ExtensionSeparatorPosition(const StringType& path) {
+ const StringType::size_type last_dot = FinalExtensionSeparatorPosition(path);
// No extension, or the extension is the whole filename.
if (last_dot == StringType::npos || last_dot == 0U)
@@ -370,6 +373,15 @@ StringType FilePath::Extension() const {
return base.path_.substr(dot, StringType::npos);
}
+StringType FilePath::FinalExtension() const {
+ FilePath base(BaseName());
+ const StringType::size_type dot = FinalExtensionSeparatorPosition(base.path_);
+ if (dot == StringType::npos)
+ return StringType();
+
+ return base.path_.substr(dot, StringType::npos);
+}
+
FilePath FilePath::RemoveExtension() const {
if (Extension().empty())
return *this;
@@ -381,6 +393,17 @@ FilePath FilePath::RemoveExtension() const {
return FilePath(path_.substr(0, dot));
}
+FilePath FilePath::RemoveFinalExtension() const {
+ if (FinalExtension().empty())
+ return *this;
+
+ const StringType::size_type dot = FinalExtensionSeparatorPosition(path_);
+ if (dot == StringType::npos)
+ return *this;
+
+ return FilePath(path_.substr(0, dot));
+}
+
FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const {
if (suffix.empty())
return FilePath(path_);
@@ -585,14 +608,6 @@ string16 FilePath::AsUTF16Unsafe() const {
#endif
}
-// The *Hack functions are temporary while we fix the remainder of the code.
-// Remember to remove the #includes at the top when you remove these.
-
-// static
-FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
- return FilePath(SysWideToNativeMB(wstring));
-}
-
// static
FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) {
#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
@@ -631,11 +646,6 @@ string16 FilePath::AsUTF16Unsafe() const {
}
// static
-FilePath FilePath::FromWStringHack(const std::wstring& wstring) {
- return FilePath(wstring);
-}
-
-// static
FilePath FilePath::FromUTF8Unsafe(const std::string& utf8) {
return FilePath(UTF8ToWide(utf8));
}
@@ -1293,6 +1303,12 @@ FilePath FilePath::NormalizePathSeparators() const {
#endif
}
+#if defined(OS_ANDROID)
+bool FilePath::IsContentUri() const {
+ return StartsWithASCII(path_, "content://", false /*case_sensitive*/);
+}
+#endif
+
} // namespace base
void PrintTo(const base::FilePath& path, std::ostream* out) {
diff --git a/chromium/base/files/file_path.h b/chromium/base/files/file_path.h
index 8b69ea493a1..f4b8ff8ade8 100644
--- a/chromium/base/files/file_path.h
+++ b/chromium/base/files/file_path.h
@@ -224,18 +224,33 @@ class BASE_EXPORT FilePath {
// Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if
// the file has no extension. If non-empty, Extension() will always start
// with precisely one ".". The following code should always work regardless
- // of the value of path.
+ // of the value of path. For common double-extensions like .tar.gz and
+ // .user.js, this method returns the combined extension. For a single
+ // component, use FinalExtension().
// new_path = path.RemoveExtension().value().append(path.Extension());
// ASSERT(new_path == path.value());
// NOTE: this is different from the original file_util implementation which
// returned the extension without a leading "." ("jpg" instead of ".jpg")
StringType Extension() const;
+ // Returns the path's file extension, as in Extension(), but will
+ // never return a double extension.
+ //
+ // 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().
+ StringType FinalExtension() const;
+
// Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg"
// NOTE: this is slightly different from the similar file_util implementation
// which returned simply 'jojo'.
FilePath RemoveExtension() const WARN_UNUSED_RESULT;
+ // Removes the path's file extension, as in RemoveExtension(), but
+ // ignores double extensions.
+ FilePath RemoveFinalExtension() const WARN_UNUSED_RESULT;
+
// Inserts |suffix| after the file name portion of |path| but before the
// extension. Returns "" if BaseName() == "." or "..".
// Examples:
@@ -332,24 +347,6 @@ class BASE_EXPORT FilePath {
// Similar to AsUTF8Unsafe, but returns UTF-16 instead.
string16 AsUTF16Unsafe() const;
- // Older Chromium code assumes that paths are always wstrings.
- // This function converts wstrings to FilePaths, and is
- // useful to smooth porting that old code to the FilePath API.
- // It has "Hack" its name so people feel bad about using it.
- // http://code.google.com/p/chromium/issues/detail?id=24672
- //
- // If you are trying to be a good citizen and remove these, ask yourself:
- // - Am I interacting with other Chrome code that deals with files? Then
- // try to convert the API into using FilePath.
- // - Am I interacting with OS-native calls? Then use value() to get at an
- // OS-native string format.
- // - Am I using well-known file names, like "config.ini"? Then use the
- // ASCII functions (we require paths to always be supersets of ASCII).
- // - Am I displaying a string to the user in some UI? Then use the
- // LossyDisplayName() function, but keep in mind that you can't
- // ever use the result of that again as a path.
- static FilePath FromWStringHack(const std::wstring& wstring);
-
// Returns a FilePath object from a path name in UTF-8. This function
// should only be used for cases where you are sure that the input
// string is UTF-8.
@@ -405,6 +402,15 @@ class BASE_EXPORT FilePath {
const StringType& string2);
#endif
+#if defined(OS_ANDROID)
+ // On android, file selection dialog can return a file with content uri
+ // scheme(starting with content://). Content uri needs to be opened with
+ // ContentResolver to guarantee that the app has appropriate permissions
+ // to access it.
+ // Returns true if the path is a content uri, or false otherwise.
+ bool IsContentUri() const;
+#endif
+
private:
// Remove trailing separators from this object. If the path is absolute, it
// will never be stripped any more than to refer to the absolute root
diff --git a/chromium/base/files/file_path_unittest.cc b/chromium/base/files/file_path_unittest.cc
index 8b2fcf531c9..fa7627c2f9b 100644
--- a/chromium/base/files/file_path_unittest.cc
+++ b/chromium/base/files/file_path_unittest.cc
@@ -708,6 +708,7 @@ TEST_F(FilePathTest, Extension) {
FilePath jpg = base_dir.Append(FILE_PATH_LITERAL("foo.jpg"));
EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.Extension());
+ EXPECT_EQ(FILE_PATH_LITERAL(".jpg"), jpg.FinalExtension());
FilePath base = jpg.BaseName().RemoveExtension();
EXPECT_EQ(FILE_PATH_LITERAL("foo"), base.value());
@@ -717,6 +718,7 @@ TEST_F(FilePathTest, Extension) {
EXPECT_EQ(path_no_ext.value(), path_no_ext.RemoveExtension().value());
EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.Extension());
+ EXPECT_EQ(FILE_PATH_LITERAL(""), path_no_ext.FinalExtension());
}
TEST_F(FilePathTest, Extension2) {
@@ -739,16 +741,9 @@ TEST_F(FilePathTest, Extension2) {
{ FPL("/foo/bar/"), FPL("") },
{ FPL("/foo/bar./"), FPL(".") },
{ FPL("/foo/bar/baz.ext1.ext2"), FPL(".ext2") },
- { FPL("/foo.tar.gz"), FPL(".tar.gz") },
- { FPL("/foo.tar.Z"), FPL(".tar.Z") },
- { FPL("/foo.tar.bz2"), FPL(".tar.bz2") },
{ FPL("/subversion-1.6.12.zip"), FPL(".zip") },
- { FPL("/foo.1234.gz"), FPL(".1234.gz") },
{ FPL("/foo.12345.gz"), FPL(".gz") },
{ FPL("/foo..gz"), FPL(".gz") },
- { FPL("/foo.1234.tar.gz"), FPL(".tar.gz") },
- { FPL("/foo.tar.tar.gz"), FPL(".tar.gz") },
- { FPL("/foo.tar.gz.gz"), FPL(".gz.gz") },
{ FPL("."), FPL("") },
{ FPL(".."), FPL("") },
{ FPL("./foo"), FPL("") },
@@ -757,14 +752,32 @@ TEST_F(FilePathTest, Extension2) {
{ FPL("/foo.bar////"), FPL(".bar") },
{ FPL("/foo.bar/.."), FPL("") },
{ FPL("/foo.bar/..////"), FPL("") },
- { FPL("/foo.1234.user.js"), FPL(".user.js") },
- { FPL("foo.user.js"), FPL(".user.js") },
{ FPL("/foo.1234.luser.js"), FPL(".js") },
{ FPL("/user.js"), FPL(".js") },
};
+ const struct UnaryTestData double_extension_cases[] = {
+ { FPL("/foo.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.Z"), FPL(".tar.Z") },
+ { FPL("/foo.tar.bz2"), FPL(".tar.bz2") },
+ { FPL("/foo.1234.gz"), FPL(".1234.gz") },
+ { FPL("/foo.1234.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.tar.gz"), FPL(".tar.gz") },
+ { FPL("/foo.tar.gz.gz"), FPL(".gz.gz") },
+ { FPL("/foo.1234.user.js"), FPL(".user.js") },
+ { FPL("foo.user.js"), FPL(".user.js") },
+ };
for (unsigned int i = 0; i < arraysize(cases); ++i) {
FilePath path(cases[i].input);
FilePath::StringType extension = path.Extension();
+ FilePath::StringType final_extension = path.FinalExtension();
+ EXPECT_STREQ(cases[i].expected, extension.c_str()) << "i: " << i <<
+ ", path: " << path.value();
+ EXPECT_STREQ(cases[i].expected, final_extension.c_str()) << "i: " << i <<
+ ", path: " << path.value();
+ }
+ for (unsigned int i = 0; i < arraysize(double_extension_cases); ++i) {
+ FilePath path(cases[i].input);
+ FilePath::StringType extension = path.Extension();
EXPECT_STREQ(cases[i].expected, extension.c_str()) << "i: " << i <<
", path: " << path.value();
}
@@ -850,7 +863,6 @@ TEST_F(FilePathTest, RemoveExtension) {
{ FPL("foo."), FPL("foo") },
{ FPL("foo.."), FPL("foo.") },
{ FPL("foo.baz.dll"), FPL("foo.baz") },
- { FPL("foo.tar.gz"), FPL("foo") },
#if defined(FILE_PATH_USES_WIN_SEPARATORS)
{ FPL("C:\\foo.bar\\foo"), FPL("C:\\foo.bar\\foo") },
{ FPL("C:\\foo.bar\\..\\\\"), FPL("C:\\foo.bar\\..\\\\") },
@@ -861,8 +873,19 @@ TEST_F(FilePathTest, RemoveExtension) {
for (unsigned int i = 0; i < arraysize(cases); ++i) {
FilePath path(cases[i].input);
FilePath removed = path.RemoveExtension();
+ FilePath removed_final = path.RemoveFinalExtension();
EXPECT_EQ(cases[i].expected, removed.value()) << "i: " << i <<
", path: " << path.value();
+ EXPECT_EQ(cases[i].expected, removed_final.value()) << "i: " << i <<
+ ", path: " << path.value();
+ }
+ {
+ FilePath path(FPL("foo.tar.gz"));
+ FilePath removed = path.RemoveExtension();
+ FilePath removed_final = path.RemoveFinalExtension();
+ EXPECT_EQ(FPL("foo"), removed.value()) << ", path: " << path.value();
+ EXPECT_EQ(FPL("foo.tar"), removed_final.value()) << ", path: "
+ << path.value();
}
}
@@ -1228,4 +1251,33 @@ TEST_F(FilePathTest, AsEndingWithSeparator) {
}
}
+#if defined(OS_ANDROID)
+TEST_F(FilePathTest, ContentUriTest) {
+ const struct UnaryBooleanTestData cases[] = {
+ { FPL("content://foo.bar"), true },
+ { FPL("content://foo.bar/"), true },
+ { FPL("content://foo/bar"), true },
+ { FPL("CoNTenT://foo.bar"), true },
+ { FPL("content://"), true },
+ { FPL("content:///foo.bar"), true },
+ { FPL("content://3foo/bar"), true },
+ { FPL("content://_foo/bar"), true },
+ { FPL(".. "), false },
+ { FPL("foo.bar"), false },
+ { FPL("content:foo.bar"), false },
+ { FPL("content:/foo.ba"), false },
+ { FPL("content:/dir/foo.bar"), false },
+ { FPL("content: //foo.bar"), false },
+ { FPL("content%2a%2f%2f"), false },
+ };
+
+ for (size_t i = 0; i < arraysize(cases); ++i) {
+ FilePath input(cases[i].input);
+ bool observed = input.IsContentUri();
+ EXPECT_EQ(cases[i].expected, observed) <<
+ "i: " << i << ", input: " << input.value();
+ }
+}
+#endif
+
} // namespace base
diff --git a/chromium/base/files/file_path_watcher_browsertest.cc b/chromium/base/files/file_path_watcher_browsertest.cc
index 69ff80608ae..aed409c7871 100644
--- a/chromium/base/files/file_path_watcher_browsertest.cc
+++ b/chromium/base/files/file_path_watcher_browsertest.cc
@@ -138,19 +138,6 @@ void SetupWatchCallback(const FilePath& target,
completion->Signal();
}
-void QuitLoopWatchCallback(MessageLoop* loop,
- const FilePath& expected_path,
- bool expected_error,
- bool* flag,
- const FilePath& path,
- bool error) {
- ASSERT_TRUE(flag);
- *flag = true;
- EXPECT_EQ(expected_path, path);
- EXPECT_EQ(expected_error, error);
- loop->PostTask(FROM_HERE, loop->QuitWhenIdleClosure());
-}
-
class FilePathWatcherTest : public testing::Test {
public:
FilePathWatcherTest()
@@ -352,7 +339,7 @@ TEST_F(FilePathWatcherTest, NonExistentDirectory) {
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WriteFile(file, "content"));
@@ -389,7 +376,7 @@ TEST_F(FilePathWatcherTest, DirectoryChain) {
for (std::vector<std::string>::const_iterator d(dir_names.begin());
d != dir_names.end(); ++d) {
sub_path = sub_path.AppendASCII(*d);
- ASSERT_TRUE(file_util::CreateDirectory(sub_path));
+ ASSERT_TRUE(base::CreateDirectory(sub_path));
}
VLOG(1) << "Create File";
ASSERT_TRUE(WriteFile(file, "content"));
@@ -410,7 +397,7 @@ TEST_F(FilePathWatcherTest, DisappearingDirectory) {
FilePathWatcher watcher;
FilePath dir(temp_dir_.path().AppendASCII("dir"));
FilePath file(dir.AppendASCII("file"));
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WriteFile(file, "content"));
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(file, &watcher, delegate.get(), false));
@@ -445,7 +432,7 @@ TEST_F(FilePathWatcherTest, WatchDirectory) {
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), false));
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
VLOG(1) << "Waiting for directory creation";
ASSERT_TRUE(WaitForEvents());
@@ -484,7 +471,7 @@ TEST_F(FilePathWatcherTest, MoveParent) {
false));
// Setup a directory hierarchy.
- ASSERT_TRUE(file_util::CreateDirectory(subdir));
+ ASSERT_TRUE(base::CreateDirectory(subdir));
ASSERT_TRUE(WriteFile(file, "content"));
VLOG(1) << "Waiting for file creation";
ASSERT_TRUE(WaitForEvents());
@@ -505,7 +492,7 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) {
ASSERT_TRUE(SetupWatch(dir, &watcher, delegate.get(), true));
// Main directory("dir") creation.
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WaitForEvents());
// Create "$dir/file1".
@@ -515,7 +502,7 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) {
// Create "$dir/subdir".
FilePath subdir(dir.AppendASCII("subdir"));
- ASSERT_TRUE(file_util::CreateDirectory(subdir));
+ ASSERT_TRUE(base::CreateDirectory(subdir));
ASSERT_TRUE(WaitForEvents());
// Create "$dir/subdir/subdir_file1".
@@ -525,7 +512,7 @@ TEST_F(FilePathWatcherTest, RecursiveWatch) {
// Create "$dir/subdir/subdir_child_dir".
FilePath subdir_child_dir(subdir.AppendASCII("subdir_child_dir"));
- ASSERT_TRUE(file_util::CreateDirectory(subdir_child_dir));
+ ASSERT_TRUE(base::CreateDirectory(subdir_child_dir));
ASSERT_TRUE(WaitForEvents());
// Create "$dir/subdir/subdir_child_dir/child_dir_file1".
@@ -572,7 +559,7 @@ TEST_F(FilePathWatcherTest, MoveChild) {
FilePath dest_file(dest_subdir.AppendASCII("file"));
// Setup a directory hierarchy.
- ASSERT_TRUE(file_util::CreateDirectory(source_subdir));
+ ASSERT_TRUE(base::CreateDirectory(source_subdir));
ASSERT_TRUE(WriteFile(source_file, "content"));
scoped_ptr<TestDelegate> file_delegate(new TestDelegate(collector()));
@@ -618,7 +605,7 @@ TEST_F(FilePathWatcherTest, CreateLink) {
// Now make sure we get notified if the link is created.
// Note that test_file() doesn't have to exist.
- ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
ASSERT_TRUE(WaitForEvents());
DeleteDelegateOnFileThread(delegate.release());
}
@@ -628,7 +615,7 @@ TEST_F(FilePathWatcherTest, DeleteLink) {
// Unfortunately this test case only works if the link target exists.
// TODO(craig) fix this as part of crbug.com/91561.
ASSERT_TRUE(WriteFile(test_file(), "content"));
- ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
FilePathWatcher watcher;
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
ASSERT_TRUE(SetupWatch(test_link(), &watcher, delegate.get(), false));
@@ -643,7 +630,7 @@ TEST_F(FilePathWatcherTest, DeleteLink) {
// when we are watching the link is caught.
TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
ASSERT_TRUE(WriteFile(test_file(), "content"));
- ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
FilePathWatcher watcher;
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
// Note that we are watching the symlink.
@@ -658,7 +645,7 @@ TEST_F(FilePathWatcherTest, ModifiedLinkedFile) {
// Verify that creating a target file that a link is pointing to
// when we are watching the link is caught.
TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
- ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
FilePathWatcher watcher;
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
// Note that we are watching the symlink.
@@ -674,7 +661,7 @@ TEST_F(FilePathWatcherTest, CreateTargetLinkedFile) {
// when we are watching the link is caught.
TEST_F(FilePathWatcherTest, DeleteTargetLinkedFile) {
ASSERT_TRUE(WriteFile(test_file(), "content"));
- ASSERT_TRUE(file_util::CreateSymbolicLink(test_file(), test_link()));
+ ASSERT_TRUE(CreateSymbolicLink(test_file(), test_link()));
FilePathWatcher watcher;
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
// Note that we are watching the symlink.
@@ -696,12 +683,12 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart1) {
FilePath linkfile(link_dir.AppendASCII("file"));
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
// dir/file should exist.
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WriteFile(file, "content"));
// Note that we are watching dir.lnk/file which doesn't exist yet.
ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
- ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
VLOG(1) << "Waiting for link creation";
ASSERT_TRUE(WaitForEvents());
@@ -726,11 +713,11 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart2) {
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
// Now create the link from dir.lnk pointing to dir but
// neither dir nor dir/file exist yet.
- ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
// Note that we are watching dir.lnk/file.
ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
- ASSERT_TRUE(file_util::CreateDirectory(dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
ASSERT_TRUE(WriteFile(file, "content"));
VLOG(1) << "Waiting for dir/file creation";
ASSERT_TRUE(WaitForEvents());
@@ -754,8 +741,8 @@ TEST_F(FilePathWatcherTest, LinkedDirectoryPart3) {
FilePath file(dir.AppendASCII("file"));
FilePath linkfile(link_dir.AppendASCII("file"));
scoped_ptr<TestDelegate> delegate(new TestDelegate(collector()));
- ASSERT_TRUE(file_util::CreateDirectory(dir));
- ASSERT_TRUE(file_util::CreateSymbolicLink(dir, link_dir));
+ ASSERT_TRUE(base::CreateDirectory(dir));
+ ASSERT_TRUE(CreateSymbolicLink(dir, link_dir));
// Note that we are watching dir.lnk/file but the file doesn't exist yet.
ASSERT_TRUE(SetupWatch(linkfile, &watcher, delegate.get(), false));
@@ -781,8 +768,8 @@ enum Permission {
Execute
};
+#if defined(OS_MACOSX)
bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
-#if defined(OS_POSIX)
struct stat stat_buf;
if (stat(path.value().c_str(), &stat_buf) != 0)
@@ -809,61 +796,8 @@ bool ChangeFilePermissions(const FilePath& path, Permission perm, bool allow) {
stat_buf.st_mode &= ~mode;
}
return chmod(path.value().c_str(), stat_buf.st_mode) == 0;
-
-#elif defined(OS_WIN)
- PACL old_dacl;
- PSECURITY_DESCRIPTOR security_descriptor;
- if (GetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
- SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl,
- NULL, &security_descriptor) != ERROR_SUCCESS)
- return false;
-
- DWORD mode = 0;
- switch (perm) {
- case Read:
- mode = GENERIC_READ;
- break;
- case Write:
- mode = GENERIC_WRITE;
- break;
- case Execute:
- mode = GENERIC_EXECUTE;
- break;
- default:
- ADD_FAILURE() << "unknown perm " << perm;
- return false;
- }
-
- // Deny Read access for the current user.
- EXPLICIT_ACCESS change;
- change.grfAccessPermissions = mode;
- change.grfAccessMode = allow ? GRANT_ACCESS : DENY_ACCESS;
- change.grfInheritance = 0;
- change.Trustee.pMultipleTrustee = NULL;
- change.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
- change.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
- change.Trustee.TrusteeType = TRUSTEE_IS_USER;
- change.Trustee.ptstrName = L"CURRENT_USER";
-
- PACL new_dacl;
- if (SetEntriesInAcl(1, &change, old_dacl, &new_dacl) != ERROR_SUCCESS) {
- LocalFree(security_descriptor);
- return false;
- }
-
- DWORD rc = SetNamedSecurityInfo(const_cast<wchar_t*>(path.value().c_str()),
- SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
- NULL, NULL, new_dacl, NULL);
- LocalFree(security_descriptor);
- LocalFree(new_dacl);
-
- return rc == ERROR_SUCCESS;
-#else
- NOTIMPLEMENTED();
- return false;
-#endif
}
+#endif // defined(OS_MACOSX)
#if defined(OS_MACOSX)
// Linux implementation of FilePathWatcher doesn't catch attribute changes.
@@ -878,8 +812,8 @@ TEST_F(FilePathWatcherTest, DirAttributesChanged) {
FilePath test_dir2(test_dir1.AppendASCII("DirAttributesChangedDir2"));
FilePath test_file(test_dir2.AppendASCII("DirAttributesChangedFile"));
// Setup a directory hierarchy.
- ASSERT_TRUE(file_util::CreateDirectory(test_dir1));
- ASSERT_TRUE(file_util::CreateDirectory(test_dir2));
+ ASSERT_TRUE(base::CreateDirectory(test_dir1));
+ ASSERT_TRUE(base::CreateDirectory(test_dir2));
ASSERT_TRUE(WriteFile(test_file, "content"));
FilePathWatcher watcher;
diff --git a/chromium/base/files/file_path_watcher_kqueue.cc b/chromium/base/files/file_path_watcher_kqueue.cc
index 2ffb83622c2..e035f22caa5 100644
--- a/chromium/base/files/file_path_watcher_kqueue.cc
+++ b/chromium/base/files/file_path_watcher_kqueue.cc
@@ -210,7 +210,7 @@ void FilePathWatcherImpl::CloseFileDescriptor(uintptr_t* fd) {
return;
}
- if (HANDLE_EINTR(close(*fd)) != 0) {
+ if (IGNORE_EINTR(close(*fd)) != 0) {
DPLOG(ERROR) << "close";
}
*fd = kNoFileDescriptor;
@@ -497,7 +497,7 @@ void FilePathWatcherImpl::CancelOnMessageLoopThread() {
if (!is_cancelled()) {
set_cancelled();
kqueue_watcher_.StopWatchingFileDescriptor();
- if (HANDLE_EINTR(close(kqueue_)) != 0) {
+ if (IGNORE_EINTR(close(kqueue_)) != 0) {
DPLOG(ERROR) << "close kqueue";
}
kqueue_ = -1;
diff --git a/chromium/base/files/file_path_watcher_linux.cc b/chromium/base/files/file_path_watcher_linux.cc
index 86a4226bf33..d5052e2e512 100644
--- a/chromium/base/files/file_path_watcher_linux.cc
+++ b/chromium/base/files/file_path_watcher_linux.cc
@@ -18,6 +18,7 @@
#include "base/bind.h"
#include "base/containers/hash_tables.h"
+#include "base/debug/trace_event.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
@@ -55,7 +56,7 @@ class InotifyReader {
void OnInotifyEvent(const inotify_event* event);
private:
- friend struct ::base::DefaultLazyInstanceTraits<InotifyReader>;
+ friend struct DefaultLazyInstanceTraits<InotifyReader>;
typedef std::set<FilePathWatcherImpl*> WatcherSet;
@@ -63,13 +64,13 @@ class InotifyReader {
~InotifyReader();
// We keep track of which delegates want to be notified on which watches.
- base::hash_map<Watch, WatcherSet> watchers_;
+ hash_map<Watch, WatcherSet> watchers_;
// Lock to protect watchers_.
- base::Lock lock_;
+ Lock lock_;
// Separate thread on which we run blocking read for inotify events.
- base::Thread thread_;
+ Thread thread_;
// File descriptor returned by inotify_init.
const int inotify_fd_;
@@ -158,6 +159,8 @@ void InotifyReaderCallback(InotifyReader* reader, int inotify_fd,
CHECK_LE(0, shutdown_fd);
CHECK_GT(FD_SETSIZE, shutdown_fd);
+ debug::TraceLog::GetInstance()->SetCurrentThreadBlocksMessageLoop();
+
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
@@ -207,18 +210,21 @@ void InotifyReaderCallback(InotifyReader* reader, int inotify_fd,
}
}
-static base::LazyInstance<InotifyReader>::Leaky g_inotify_reader =
+static LazyInstance<InotifyReader>::Leaky g_inotify_reader =
LAZY_INSTANCE_INITIALIZER;
InotifyReader::InotifyReader()
: thread_("inotify_reader"),
inotify_fd_(inotify_init()),
valid_(false) {
+ if (inotify_fd_ < 0)
+ PLOG(ERROR) << "inotify_init() failed";
+
shutdown_pipe_[0] = -1;
shutdown_pipe_[1] = -1;
if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) {
thread_.message_loop()->PostTask(
- FROM_HERE, base::Bind(&InotifyReaderCallback, this, inotify_fd_,
+ FROM_HERE, Bind(&InotifyReaderCallback, this, inotify_fd_,
shutdown_pipe_[0]));
valid_ = true;
}
@@ -246,7 +252,7 @@ InotifyReader::Watch InotifyReader::AddWatch(
if (!valid_)
return kInvalidWatch;
- base::AutoLock auto_lock(lock_);
+ AutoLock auto_lock(lock_);
Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(),
IN_CREATE | IN_DELETE |
@@ -266,7 +272,7 @@ bool InotifyReader::RemoveWatch(Watch watch,
if (!valid_)
return false;
- base::AutoLock auto_lock(lock_);
+ AutoLock auto_lock(lock_);
watchers_[watch].erase(watcher);
@@ -283,7 +289,7 @@ void InotifyReader::OnInotifyEvent(const inotify_event* event) {
return;
FilePath::StringType child(event->len ? event->name : FILE_PATH_LITERAL(""));
- base::AutoLock auto_lock(lock_);
+ AutoLock auto_lock(lock_);
for (WatcherSet::iterator watcher = watchers_[event->wd].begin();
watcher != watchers_[event->wd].end();
@@ -303,7 +309,7 @@ void FilePathWatcherImpl::OnFilePathChanged(InotifyReader::Watch fired_watch,
if (!message_loop()->BelongsToCurrentThread()) {
// Switch to message_loop_ to access watches_ safely.
message_loop()->PostTask(FROM_HERE,
- base::Bind(&FilePathWatcherImpl::OnFilePathChanged,
+ Bind(&FilePathWatcherImpl::OnFilePathChanged,
this,
fired_watch,
child,
@@ -370,7 +376,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
return false;
}
- set_message_loop(base::MessageLoopProxy::current().get());
+ set_message_loop(MessageLoopProxy::current().get());
callback_ = callback;
target_ = path;
MessageLoop::current()->AddDestructionObserver(this);
@@ -397,7 +403,7 @@ void FilePathWatcherImpl::Cancel() {
// Switch to the message_loop_ if necessary so we can access |watches_|.
if (!message_loop()->BelongsToCurrentThread()) {
message_loop()->PostTask(FROM_HERE,
- base::Bind(&FilePathWatcher::CancelWatch,
+ Bind(&FilePathWatcher::CancelWatch,
make_scoped_refptr(this)));
} else {
CancelOnMessageLoopThread();
@@ -440,9 +446,9 @@ bool FilePathWatcherImpl::UpdateWatches() {
if (path_valid) {
watch_entry->watch_ = g_inotify_reader.Get().AddWatch(path, this);
if ((watch_entry->watch_ == InotifyReader::kInvalidWatch) &&
- file_util::IsLink(path)) {
+ base::IsLink(path)) {
FilePath link;
- if (file_util::ReadSymbolicLink(path, &link)) {
+ if (ReadSymbolicLink(path, &link)) {
if (!link.IsAbsolute())
link = path.DirName().Append(link);
// Try watching symlink target directory. If the link target is "/",
diff --git a/chromium/base/files/file_path_watcher_win.cc b/chromium/base/files/file_path_watcher_win.cc
index ac092a931b9..2abbceacd29 100644
--- a/chromium/base/files/file_path_watcher_win.cc
+++ b/chromium/base/files/file_path_watcher_win.cc
@@ -76,11 +76,11 @@ class FilePathWatcherImpl : public FilePathWatcher::PlatformDelegate,
// Keep track of the last modified time of the file. We use nulltime
// to represent the file not existing.
- base::Time last_modified_;
+ Time last_modified_;
// The time at which we processed the first notification with the
// |last_modified_| time stamp.
- base::Time first_notification_;
+ Time first_notification_;
DISALLOW_COPY_AND_ASSIGN(FilePathWatcherImpl);
};
@@ -90,7 +90,7 @@ bool FilePathWatcherImpl::Watch(const FilePath& path,
const FilePathWatcher::Callback& callback) {
DCHECK(target_.value().empty()); // Can only watch one path.
- set_message_loop(base::MessageLoopProxy::current());
+ set_message_loop(MessageLoopProxy::current());
callback_ = callback;
target_ = path;
recursive_watch_ = recursive;
@@ -114,8 +114,8 @@ void FilePathWatcherImpl::Cancel() {
// Switch to the file thread if necessary so we can stop |watcher_|.
if (!message_loop()->BelongsToCurrentThread()) {
message_loop()->PostTask(FROM_HERE,
- base::Bind(&FilePathWatcher::CancelWatch,
- make_scoped_refptr(this)));
+ Bind(&FilePathWatcher::CancelWatch,
+ make_scoped_refptr(this)));
} else {
CancelOnMessageLoopThread();
}
@@ -148,12 +148,12 @@ void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
}
// Check whether the event applies to |target_| and notify the callback.
- base::PlatformFileInfo file_info;
- bool file_exists = file_util::GetFileInfo(target_, &file_info);
+ PlatformFileInfo file_info;
+ bool file_exists = GetFileInfo(target_, &file_info);
if (file_exists && (last_modified_.is_null() ||
last_modified_ != file_info.last_modified)) {
last_modified_ = file_info.last_modified;
- first_notification_ = base::Time::Now();
+ first_notification_ = Time::Now();
callback_.Run(target_, false);
} else if (file_exists && !first_notification_.is_null()) {
// The target's last modification time is equal to what's on record. This
@@ -170,14 +170,13 @@ void FilePathWatcherImpl::OnObjectSignaled(HANDLE object) {
// clock has advanced one second from the initial notification. After that
// interval, client code is guaranteed to having seen the current revision
// of the file.
- if (base::Time::Now() - first_notification_ >
- base::TimeDelta::FromSeconds(1)) {
+ if (Time::Now() - first_notification_ > TimeDelta::FromSeconds(1)) {
// Stop further notifications for this |last_modification_| time stamp.
- first_notification_ = base::Time();
+ first_notification_ = Time();
}
callback_.Run(target_, false);
} else if (!file_exists && !last_modified_.is_null()) {
- last_modified_ = base::Time();
+ last_modified_ = Time();
callback_.Run(target_, false);
}
@@ -230,10 +229,10 @@ bool FilePathWatcherImpl::UpdateWatch() {
if (handle_ != INVALID_HANDLE_VALUE)
DestroyWatch();
- base::PlatformFileInfo file_info;
- if (file_util::GetFileInfo(target_, &file_info)) {
+ PlatformFileInfo file_info;
+ if (GetFileInfo(target_, &file_info)) {
last_modified_ = file_info.last_modified;
- first_notification_ = base::Time::Now();
+ first_notification_ = Time::Now();
}
// Start at the target and walk up the directory chain until we succesfully
diff --git a/chromium/base/files/file_posix.cc b/chromium/base/files/file_posix.cc
new file mode 100644
index 00000000000..9d97c336aa6
--- /dev/null
+++ b/chromium/base/files/file_posix.cc
@@ -0,0 +1,470 @@
+// Copyright (c) 2012 The Chromium Authors. All 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.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+// TODO(rvargas): remove this (needed for kInvalidPlatformFileValue).
+#include "base/platform_file.h"
+#include "base/posix/eintr_wrapper.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/threading/thread_restrictions.h"
+
+#if defined(OS_ANDROID)
+#include "base/os_compat_android.h"
+#endif
+
+namespace base {
+
+// Make sure our Whence mappings match the system headers.
+COMPILE_ASSERT(File::FROM_BEGIN == SEEK_SET &&
+ File::FROM_CURRENT == SEEK_CUR &&
+ File::FROM_END == SEEK_END, whence_matches_system);
+
+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);
+}
+#endif
+
+// NaCl doesn't provide the following system calls, so either simulate them or
+// wrap them in order to minimize the number of #ifdef's in this file.
+#if !defined(OS_NACL)
+static bool IsOpenAppend(PlatformFile file) {
+ return (fcntl(file, F_GETFL) & O_APPEND) != 0;
+}
+
+static int CallFtruncate(PlatformFile file, int64 length) {
+ return HANDLE_EINTR(ftruncate(file, length));
+}
+
+static int CallFsync(PlatformFile file) {
+ return HANDLE_EINTR(fsync(file));
+}
+
+static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
+#ifdef __USE_XOPEN2K8
+ // futimens should be available, but futimes might not be
+ // http://pubs.opengroup.org/onlinepubs/9699919799/
+
+ timespec ts_times[2];
+ ts_times[0].tv_sec = times[0].tv_sec;
+ ts_times[0].tv_nsec = times[0].tv_usec * 1000;
+ ts_times[1].tv_sec = times[1].tv_sec;
+ ts_times[1].tv_nsec = times[1].tv_usec * 1000;
+
+ return futimens(file, ts_times);
+#else
+ return futimes(file, times);
+#endif
+}
+
+static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; // Lock entire file.
+ if (HANDLE_EINTR(fcntl(file, do_lock ? F_SETLK : F_UNLCK, &lock)) == -1)
+ return File::OSErrorToFileError(errno);
+ return File::FILE_OK;
+}
+#else // defined(OS_NACL)
+
+static bool IsOpenAppend(PlatformFile file) {
+ // NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
+ // standard and always appends if the file is opened with O_APPEND, just
+ // return false here.
+ return false;
+}
+
+static int CallFtruncate(PlatformFile file, int64 length) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement ftruncate.
+ return 0;
+}
+
+static int CallFsync(PlatformFile file) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement fsync.
+ return 0;
+}
+
+static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
+ return 0;
+}
+
+static File::Error CallFctnlFlock(PlatformFile file, bool do_lock) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
+ return File::FILE_ERROR_INVALID_OPERATION;
+}
+#endif // defined(OS_NACL)
+
+} // namespace
+
+// 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) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(!IsValid());
+ DCHECK(!(flags & FLAG_ASYNC));
+
+ int open_flags = 0;
+ if (flags & FLAG_CREATE)
+ open_flags = O_CREAT | O_EXCL;
+
+ created_ = false;
+
+ if (flags & FLAG_CREATE_ALWAYS) {
+ DCHECK(!open_flags);
+ open_flags = O_CREAT | O_TRUNC;
+ }
+
+ if (flags & FLAG_OPEN_TRUNCATED) {
+ DCHECK(!open_flags);
+ DCHECK(flags & FLAG_WRITE);
+ open_flags = O_TRUNC;
+ }
+
+ if (!open_flags && !(flags & FLAG_OPEN) && !(flags & FLAG_OPEN_ALWAYS)) {
+ NOTREACHED();
+ errno = EOPNOTSUPP;
+ error_ = FILE_ERROR_FAILED;
+ return;
+ }
+
+ if (flags & FLAG_WRITE && flags & FLAG_READ) {
+ open_flags |= O_RDWR;
+ } else if (flags & FLAG_WRITE) {
+ open_flags |= O_WRONLY;
+ } else if (!(flags & FLAG_READ) &&
+ !(flags & FLAG_WRITE_ATTRIBUTES) &&
+ !(flags & FLAG_APPEND) &&
+ !(flags & FLAG_OPEN_ALWAYS)) {
+ NOTREACHED();
+ }
+
+ if (flags & FLAG_TERMINAL_DEVICE)
+ open_flags |= O_NOCTTY | O_NDELAY;
+
+ if (flags & FLAG_APPEND && flags & FLAG_READ)
+ open_flags |= O_APPEND | O_RDWR;
+ else if (flags & FLAG_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 & FLAG_OPEN_ALWAYS) {
+ if (descriptor < 0) {
+ open_flags |= O_CREAT;
+ if (flags & FLAG_EXCLUSIVE_READ || flags & FLAG_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 (descriptor >= 0)
+ created_ = true;
+ }
+ }
+
+ if (descriptor >= 0 && (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE)))
+ created_ = true;
+
+ if ((descriptor >= 0) && (flags & FLAG_DELETE_ON_CLOSE))
+ unlink(name.value().c_str());
+
+ if (descriptor >= 0)
+ error_ = FILE_OK;
+ else
+ error_ = File::OSErrorToFileError(errno);
+
+ file_ = descriptor;
+}
+#endif // !defined(OS_NACL)
+
+bool File::IsValid() const {
+ return file_ >= 0;
+}
+
+PlatformFile File::TakePlatformFile() {
+ PlatformFile file = file_;
+ file_ = kInvalidPlatformFileValue;
+ return file;
+}
+
+void File::Close() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (!IsValid())
+ return;
+
+ if (!IGNORE_EINTR(close(file_)))
+ file_ = kInvalidPlatformFileValue;
+}
+
+int64 File::Seek(Whence whence, int64 offset) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (file_ < 0 || offset < 0)
+ return -1;
+
+ return lseek(file_, static_cast<off_t>(offset), static_cast<int>(whence));
+}
+
+int File::Read(int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pread(file_, data + bytes_read,
+ size - bytes_read, offset + bytes_read));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+int File::ReadAtCurrentPos(char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_read = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(read(file_, data, size));
+ if (rv <= 0)
+ break;
+
+ bytes_read += rv;
+ } while (bytes_read < size);
+
+ return bytes_read ? bytes_read : rv;
+}
+
+int File::ReadNoBestEffort(int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+
+ return HANDLE_EINTR(pread(file_, data, size, offset));
+}
+
+int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ return HANDLE_EINTR(read(file_, data, size));
+}
+
+int File::Write(int64 offset, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+
+ if (IsOpenAppend(file_))
+ return WriteAtCurrentPos(data, size);
+
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(pwrite(file_, data + bytes_written,
+ size - bytes_written, offset + bytes_written));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+
+int File::WriteAtCurrentPos(const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ int bytes_written = 0;
+ int rv;
+ do {
+ rv = HANDLE_EINTR(write(file_, data, size));
+ if (rv <= 0)
+ break;
+
+ bytes_written += rv;
+ } while (bytes_written < size);
+
+ return bytes_written ? bytes_written : rv;
+}
+
+int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (size < 0)
+ return -1;
+
+ return HANDLE_EINTR(write(file_, data, size));
+}
+
+bool File::Truncate(int64 length) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ return !CallFtruncate(file_, length);
+}
+
+bool File::Flush() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ return !CallFsync(file_);
+}
+
+bool File::SetTimes(Time last_access_time, Time last_modified_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+
+ timeval times[2];
+ times[0] = last_access_time.ToTimeVal();
+ times[1] = last_modified_time.ToTimeVal();
+
+ return !CallFutimes(file_, times);
+}
+
+bool File::GetInfo(Info* info) {
+ DCHECK(IsValid());
+
+ stat_wrapper_t file_info;
+ if (CallFstat(file_, &file_info))
+ return false;
+
+ info->is_directory = S_ISDIR(file_info.st_mode);
+ info->is_symbolic_link = S_ISLNK(file_info.st_mode);
+ info->size = file_info.st_size;
+
+#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);
+ return true;
+}
+
+File::Error File::Lock() {
+ return CallFctnlFlock(file_, true);
+}
+
+File::Error File::Unlock() {
+ return CallFctnlFlock(file_, false);
+}
+
+// Static.
+File::Error File::OSErrorToFileError(int saved_errno) {
+ switch (saved_errno) {
+ case EACCES:
+ case EISDIR:
+ case EROFS:
+ case EPERM:
+ return FILE_ERROR_ACCESS_DENIED;
+#if !defined(OS_NACL) // ETXTBSY not defined by NaCl.
+ case ETXTBSY:
+ return FILE_ERROR_IN_USE;
+#endif
+ case EEXIST:
+ return FILE_ERROR_EXISTS;
+ case ENOENT:
+ return FILE_ERROR_NOT_FOUND;
+ case EMFILE:
+ return FILE_ERROR_TOO_MANY_OPENED;
+ case ENOMEM:
+ return FILE_ERROR_NO_MEMORY;
+ case ENOSPC:
+ return FILE_ERROR_NO_SPACE;
+ case ENOTDIR:
+ return FILE_ERROR_NOT_A_DIRECTORY;
+ default:
+#if !defined(OS_NACL) // NaCl build has no metrics code.
+ UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Posix",
+ saved_errno);
+#endif
+ return FILE_ERROR_FAILED;
+ }
+}
+
+void File::SetPlatformFile(PlatformFile file) {
+ DCHECK_EQ(file_, kInvalidPlatformFileValue);
+ file_ = file;
+}
+
+} // namespace base
diff --git a/chromium/base/files/file_unittest.cc b/chromium/base/files/file_unittest.cc
new file mode 100644
index 00000000000..b2e855da1a0
--- /dev/null
+++ b/chromium/base/files/file_unittest.cc
@@ -0,0 +1,360 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::File;
+using base::FilePath;
+
+TEST(File, Create) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
+
+ {
+ // Open a file that doesn't exist.
+ 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());
+ }
+
+ {
+ // Open or create a file.
+ 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());
+ }
+
+ {
+ // Open an existing file.
+ 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());
+
+ // This time verify closing the file.
+ file.Close();
+ EXPECT_FALSE(file.IsValid());
+ }
+
+ {
+ // Create a file that exists.
+ 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());
+ }
+
+ {
+ // Create or overwrite a file.
+ File file(file_path,
+ base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_READ);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+ EXPECT_EQ(base::File::FILE_OK, file.error());
+ }
+
+ {
+ // Create a delete-on-close file.
+ file_path = temp_dir.path().AppendASCII("create_file_2");
+ File file(file_path,
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_DELETE_ON_CLOSE);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+ EXPECT_EQ(base::File::FILE_OK, file.error());
+ }
+
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+TEST(File, DeleteOpenFile) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
+
+ // Create a file.
+ File file(file_path,
+ base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ |
+ base::File::FLAG_SHARE_DELETE);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_TRUE(file.created());
+ EXPECT_EQ(base::File::FILE_OK, file.error());
+
+ // Open an existing file and mark it as delete on close.
+ File same_file(file_path,
+ base::File::FLAG_OPEN | base::File::FLAG_DELETE_ON_CLOSE |
+ base::File::FLAG_READ);
+ EXPECT_TRUE(file.IsValid());
+ EXPECT_FALSE(same_file.created());
+ EXPECT_EQ(base::File::FILE_OK, same_file.error());
+
+ // Close both handles and check that the file is gone.
+ file.Close();
+ same_file.Close();
+ EXPECT_FALSE(base::PathExists(file_path));
+}
+
+TEST(File, ReadWrite) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
+ File file(file_path,
+ base::File::FLAG_CREATE | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+
+ char data_to_write[] = "test";
+ const int kTestDataSize = 4;
+
+ // Write 0 bytes to the file.
+ int bytes_written = file.Write(0, data_to_write, 0);
+ EXPECT_EQ(0, bytes_written);
+
+ // Write "test" to the file.
+ bytes_written = file.Write(0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Read from EOF.
+ char data_read_1[32];
+ int bytes_read = file.Read(kTestDataSize, data_read_1, kTestDataSize);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read from somewhere in the middle of the file.
+ const int kPartialReadOffset = 1;
+ bytes_read = file.Read(kPartialReadOffset, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize - kPartialReadOffset, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i + kPartialReadOffset], data_read_1[i]);
+
+ // Read 0 bytes.
+ bytes_read = file.Read(0, data_read_1, 0);
+ EXPECT_EQ(0, bytes_read);
+
+ // Read the entire file.
+ bytes_read = file.Read(0, data_read_1, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_read);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+
+ // Read again, but using the trivial native wrapper.
+ bytes_read = file.ReadNoBestEffort(0, data_read_1, kTestDataSize);
+ EXPECT_LE(bytes_read, kTestDataSize);
+ for (int i = 0; i < bytes_read; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+
+ // Write past the end of the file.
+ const int kOffsetBeyondEndOfFile = 10;
+ const int kPartialWriteLength = 2;
+ bytes_written = file.Write(kOffsetBeyondEndOfFile,
+ data_to_write, kPartialWriteLength);
+ EXPECT_EQ(kPartialWriteLength, bytes_written);
+
+ // Make sure the file was extended.
+ int64 file_size = 0;
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read_2[32];
+ bytes_read = file.Read(0, data_read_2, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read_2[i]);
+ for (int i = kTestDataSize; i < kOffsetBeyondEndOfFile; i++)
+ EXPECT_EQ(0, data_read_2[i]);
+ for (int i = kOffsetBeyondEndOfFile; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
+}
+
+TEST(File, Append) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("append_file");
+ File file(file_path, base::File::FLAG_CREATE | base::File::FLAG_APPEND);
+ ASSERT_TRUE(file.IsValid());
+
+ char data_to_write[] = "test";
+ const int kTestDataSize = 4;
+
+ // Write 0 bytes to the file.
+ int bytes_written = file.Write(0, data_to_write, 0);
+ EXPECT_EQ(0, bytes_written);
+
+ // Write "test" to the file.
+ bytes_written = file.Write(0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ file.Close();
+ File file2(file_path,
+ base::File::FLAG_OPEN | base::File::FLAG_READ |
+ base::File::FLAG_APPEND);
+ ASSERT_TRUE(file2.IsValid());
+
+ // Test passing the file around.
+ file = file2.Pass();
+ EXPECT_FALSE(file2.IsValid());
+ ASSERT_TRUE(file.IsValid());
+
+ char append_data_to_write[] = "78";
+ const int kAppendDataSize = 2;
+
+ // Append "78" to the file.
+ bytes_written = file.Write(0, append_data_to_write, kAppendDataSize);
+ EXPECT_EQ(kAppendDataSize, bytes_written);
+
+ // Read the entire file.
+ char data_read_1[32];
+ int bytes_read = file.Read(0, data_read_1,
+ kTestDataSize + kAppendDataSize);
+ EXPECT_EQ(kTestDataSize + kAppendDataSize, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read_1[i]);
+ for (int i = 0; i < kAppendDataSize; i++)
+ EXPECT_EQ(append_data_to_write[i], data_read_1[kTestDataSize + i]);
+}
+
+
+TEST(File, Truncate) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
+ File file(file_path,
+ base::File::FLAG_CREATE | base::File::FLAG_READ |
+ base::File::FLAG_WRITE);
+ ASSERT_TRUE(file.IsValid());
+
+ // Write "test" to the file.
+ char data_to_write[] = "test";
+ int kTestDataSize = 4;
+ int bytes_written = file.Write(0, data_to_write, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Extend the file.
+ const int kExtendedFileLength = 10;
+ int64 file_size = 0;
+ EXPECT_TRUE(file.Truncate(kExtendedFileLength));
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kExtendedFileLength, file_size);
+
+ // Make sure the file was zero-padded.
+ char data_read[32];
+ int bytes_read = file.Read(0, data_read, static_cast<int>(file_size));
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < kTestDataSize; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+ for (int i = kTestDataSize; i < file_size; i++)
+ EXPECT_EQ(0, data_read[i]);
+
+ // Truncate the file.
+ const int kTruncatedFileLength = 2;
+ EXPECT_TRUE(file.Truncate(kTruncatedFileLength));
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
+ EXPECT_EQ(kTruncatedFileLength, file_size);
+
+ // Make sure the file was truncated.
+ bytes_read = file.Read(0, data_read, kTestDataSize);
+ EXPECT_EQ(file_size, bytes_read);
+ for (int i = 0; i < file_size; i++)
+ EXPECT_EQ(data_to_write[i], data_read[i]);
+}
+
+// Flakily fails: http://crbug.com/86494
+#if defined(OS_ANDROID)
+TEST(File, TouchGetInfo) {
+#else
+TEST(File, DISABLED_TouchGetInfo) {
+#endif
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ File file(temp_dir.path().AppendASCII("touch_get_info_file"),
+ base::File::FLAG_CREATE | base::File::FLAG_WRITE |
+ base::File::FLAG_WRITE_ATTRIBUTES);
+ ASSERT_TRUE(file.IsValid());
+
+ // Get info for a newly created file.
+ base::File::Info info;
+ EXPECT_TRUE(file.GetInfo(&info));
+
+ // Add 2 seconds to account for possible rounding errors on
+ // filesystems that use a 1s or 2s timestamp granularity.
+ base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2);
+ EXPECT_EQ(0, info.size);
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+ EXPECT_LE(info.last_accessed.ToInternalValue(), now.ToInternalValue());
+ EXPECT_LE(info.last_modified.ToInternalValue(), now.ToInternalValue());
+ EXPECT_LE(info.creation_time.ToInternalValue(), now.ToInternalValue());
+ base::Time creation_time = info.creation_time;
+
+ // Write "test" to the file.
+ char data[] = "test";
+ const int kTestDataSize = 4;
+ int bytes_written = file.Write(0, data, kTestDataSize);
+ EXPECT_EQ(kTestDataSize, bytes_written);
+
+ // Change the last_accessed and last_modified dates.
+ // It's best to add values that are multiples of 2 (in seconds)
+ // to the current last_accessed and last_modified times, because
+ // FATxx uses a 2s timestamp granularity.
+ base::Time new_last_accessed =
+ info.last_accessed + base::TimeDelta::FromSeconds(234);
+ base::Time new_last_modified =
+ info.last_modified + base::TimeDelta::FromMinutes(567);
+
+ EXPECT_TRUE(file.SetTimes(new_last_accessed, new_last_modified));
+
+ // Make sure the file info was updated accordingly.
+ EXPECT_TRUE(file.GetInfo(&info));
+ EXPECT_EQ(info.size, kTestDataSize);
+ EXPECT_FALSE(info.is_directory);
+ EXPECT_FALSE(info.is_symbolic_link);
+
+ // ext2/ext3 and HPS/HPS+ seem to have a timestamp granularity of 1s.
+#if defined(OS_POSIX)
+ EXPECT_EQ(info.last_accessed.ToTimeVal().tv_sec,
+ new_last_accessed.ToTimeVal().tv_sec);
+ EXPECT_EQ(info.last_modified.ToTimeVal().tv_sec,
+ new_last_modified.ToTimeVal().tv_sec);
+#else
+ EXPECT_EQ(info.last_accessed.ToInternalValue(),
+ new_last_accessed.ToInternalValue());
+ EXPECT_EQ(info.last_modified.ToInternalValue(),
+ new_last_modified.ToInternalValue());
+#endif
+
+ EXPECT_EQ(info.creation_time.ToInternalValue(),
+ creation_time.ToInternalValue());
+}
+
+TEST(File, ReadFileAtCurrentPosition) {
+ base::ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path =
+ temp_dir.path().AppendASCII("read_file_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;
+ EXPECT_EQ(kDataSize, file.Write(0, kData, kDataSize));
+
+ EXPECT_EQ(0, file.Seek(base::File::FROM_BEGIN, 0));
+
+ char buffer[kDataSize];
+ int first_chunk_size = kDataSize / 2;
+ EXPECT_EQ(first_chunk_size, file.ReadAtCurrentPos(buffer, first_chunk_size));
+ 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));
+}
diff --git a/chromium/base/files/file_util_proxy.cc b/chromium/base/files/file_util_proxy.cc
index 5f6d405a905..40cac112820 100644
--- a/chromium/base/files/file_util_proxy.cc
+++ b/chromium/base/files/file_util_proxy.cc
@@ -76,7 +76,7 @@ class CreateTemporaryHelper {
void RunWork(int additional_file_flags) {
// TODO(darin): file_util should have a variant of CreateTemporaryFile
// that returns a FilePath and a PlatformFile.
- file_util::CreateTemporaryFile(&file_path_);
+ base::CreateTemporaryFile(&file_path_);
int file_flags =
PLATFORM_FILE_WRITE |
@@ -111,7 +111,7 @@ class GetFileInfoHelper {
error_ = PLATFORM_FILE_ERROR_NOT_FOUND;
return;
}
- if (!file_util::GetFileInfo(file_path, &file_info_))
+ if (!GetFileInfo(file_path, &file_info_))
error_ = PLATFORM_FILE_ERROR_FAILED;
}
@@ -213,7 +213,7 @@ PlatformFileError DeleteAdapter(const FilePath& file_path, bool recursive) {
return PLATFORM_FILE_ERROR_NOT_FOUND;
}
if (!base::DeleteFile(file_path, recursive)) {
- if (!recursive && !file_util::IsDirectoryEmpty(file_path)) {
+ if (!recursive && !base::IsDirectoryEmpty(file_path)) {
return PLATFORM_FILE_ERROR_NOT_EMPTY;
}
return PLATFORM_FILE_ERROR_FAILED;
@@ -357,8 +357,7 @@ bool FileUtilProxy::Touch(
return base::PostTaskAndReplyWithResult(
task_runner,
FROM_HERE,
- Bind(&file_util::TouchFile, file_path,
- last_access_time, last_modified_time),
+ Bind(&TouchFile, file_path, last_access_time, last_modified_time),
Bind(&CallWithTranslatedParameter, callback));
}
diff --git a/chromium/base/files/file_util_proxy_unittest.cc b/chromium/base/files/file_util_proxy_unittest.cc
index 73ac8a60474..17c7a3f7cf5 100644
--- a/chromium/base/files/file_util_proxy_unittest.cc
+++ b/chromium/base/files/file_util_proxy_unittest.cc
@@ -226,7 +226,7 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) {
// Setup.
ASSERT_EQ(4, file_util::WriteFile(test_path(), "test", 4));
PlatformFileInfo expected_info;
- file_util::GetFileInfo(test_path(), &expected_info);
+ GetFileInfo(test_path(), &expected_info);
// Run.
FileUtilProxy::GetFileInfo(
@@ -247,9 +247,9 @@ TEST_F(FileUtilProxyTest, GetFileInfo_File) {
TEST_F(FileUtilProxyTest, GetFileInfo_Directory) {
// Setup.
- ASSERT_TRUE(file_util::CreateDirectory(test_path()));
+ ASSERT_TRUE(base::CreateDirectory(test_path()));
PlatformFileInfo expected_info;
- file_util::GetFileInfo(test_path(), &expected_info);
+ GetFileInfo(test_path(), &expected_info);
// Run.
FileUtilProxy::GetFileInfo(
@@ -320,7 +320,7 @@ TEST_F(FileUtilProxyTest, WriteAndFlush) {
// Verify the written data.
char buffer[10];
- EXPECT_EQ(data_bytes, file_util::ReadFile(test_path(), buffer, data_bytes));
+ 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]);
}
@@ -342,7 +342,7 @@ TEST_F(FileUtilProxyTest, Touch) {
EXPECT_EQ(PLATFORM_FILE_OK, error_);
PlatformFileInfo info;
- file_util::GetFileInfo(test_path(), &info);
+ GetFileInfo(test_path(), &info);
// The returned values may only have the seconds precision, so we cast
// the double values to int here.
@@ -357,7 +357,7 @@ TEST_F(FileUtilProxyTest, Truncate_Shrink) {
const char kTestData[] = "0123456789";
ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10));
PlatformFileInfo info;
- file_util::GetFileInfo(test_path(), &info);
+ GetFileInfo(test_path(), &info);
ASSERT_EQ(10, info.size);
// Run.
@@ -369,11 +369,11 @@ TEST_F(FileUtilProxyTest, Truncate_Shrink) {
MessageLoop::current()->Run();
// Verify.
- file_util::GetFileInfo(test_path(), &info);
+ GetFileInfo(test_path(), &info);
ASSERT_EQ(7, info.size);
char buffer[7];
- EXPECT_EQ(7, file_util::ReadFile(test_path(), buffer, 7));
+ EXPECT_EQ(7, base::ReadFile(test_path(), buffer, 7));
int i = 0;
for (; i < 7; ++i)
EXPECT_EQ(kTestData[i], buffer[i]);
@@ -384,7 +384,7 @@ TEST_F(FileUtilProxyTest, Truncate_Expand) {
const char kTestData[] = "9876543210";
ASSERT_EQ(10, file_util::WriteFile(test_path(), kTestData, 10));
PlatformFileInfo info;
- file_util::GetFileInfo(test_path(), &info);
+ GetFileInfo(test_path(), &info);
ASSERT_EQ(10, info.size);
// Run.
@@ -396,11 +396,11 @@ TEST_F(FileUtilProxyTest, Truncate_Expand) {
MessageLoop::current()->Run();
// Verify.
- file_util::GetFileInfo(test_path(), &info);
+ GetFileInfo(test_path(), &info);
ASSERT_EQ(53, info.size);
char buffer[53];
- EXPECT_EQ(53, file_util::ReadFile(test_path(), buffer, 53));
+ EXPECT_EQ(53, base::ReadFile(test_path(), buffer, 53));
int i = 0;
for (; i < 10; ++i)
EXPECT_EQ(kTestData[i], buffer[i]);
diff --git a/chromium/base/files/file_win.cc b/chromium/base/files/file_win.cc
new file mode 100644
index 00000000000..94f4d7f59c9
--- /dev/null
+++ b/chromium/base/files/file_win.cc
@@ -0,0 +1,319 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/files/file.h"
+
+#include <io.h>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/threading/thread_restrictions.h"
+
+namespace base {
+
+void File::CreateBaseFileUnsafe(const FilePath& name, uint32 flags) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(!IsValid());
+
+ DWORD disposition = 0;
+
+ if (flags & FLAG_OPEN)
+ disposition = OPEN_EXISTING;
+
+ if (flags & FLAG_CREATE) {
+ DCHECK(!disposition);
+ disposition = CREATE_NEW;
+ }
+
+ if (flags & FLAG_OPEN_ALWAYS) {
+ DCHECK(!disposition);
+ disposition = OPEN_ALWAYS;
+ }
+
+ if (flags & FLAG_CREATE_ALWAYS) {
+ DCHECK(!disposition);
+ disposition = CREATE_ALWAYS;
+ }
+
+ if (flags & FLAG_OPEN_TRUNCATED) {
+ DCHECK(!disposition);
+ DCHECK(flags & FLAG_WRITE);
+ disposition = TRUNCATE_EXISTING;
+ }
+
+ if (!disposition) {
+ NOTREACHED();
+ return;
+ }
+
+ DWORD access = 0;
+ if (flags & FLAG_WRITE)
+ access = GENERIC_WRITE;
+ if (flags & FLAG_APPEND) {
+ DCHECK(!access);
+ access = FILE_APPEND_DATA;
+ }
+ if (flags & FLAG_READ)
+ access |= GENERIC_READ;
+ if (flags & FLAG_WRITE_ATTRIBUTES)
+ access |= FILE_WRITE_ATTRIBUTES;
+ if (flags & FLAG_EXECUTE)
+ access |= GENERIC_EXECUTE;
+
+ DWORD sharing = (flags & FLAG_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ;
+ if (!(flags & FLAG_EXCLUSIVE_WRITE))
+ sharing |= FILE_SHARE_WRITE;
+ if (flags & FLAG_SHARE_DELETE)
+ sharing |= FILE_SHARE_DELETE;
+
+ DWORD create_flags = 0;
+ if (flags & FLAG_ASYNC)
+ create_flags |= FILE_FLAG_OVERLAPPED;
+ if (flags & FLAG_TEMPORARY)
+ create_flags |= FILE_ATTRIBUTE_TEMPORARY;
+ if (flags & FLAG_HIDDEN)
+ create_flags |= FILE_ATTRIBUTE_HIDDEN;
+ if (flags & FLAG_DELETE_ON_CLOSE)
+ create_flags |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (flags & FLAG_BACKUP_SEMANTICS)
+ create_flags |= FILE_FLAG_BACKUP_SEMANTICS;
+
+ file_.Set(CreateFile(name.value().c_str(), access, sharing, NULL,
+ disposition, create_flags, NULL));
+
+ if (file_.IsValid()) {
+ error_ = FILE_OK;
+ async_ = ((flags & FLAG_ASYNC) == FLAG_ASYNC);
+
+ if (flags & (FLAG_OPEN_ALWAYS))
+ created_ = (ERROR_ALREADY_EXISTS != GetLastError());
+ else if (flags & (FLAG_CREATE_ALWAYS | FLAG_CREATE))
+ created_ = true;
+ } else {
+ error_ = OSErrorToFileError(GetLastError());
+ }
+}
+
+bool File::IsValid() const {
+ return file_.IsValid();
+}
+PlatformFile File::TakePlatformFile() {
+ return file_.Take();
+}
+
+void File::Close() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ file_.Close();
+}
+
+int64 File::Seek(Whence whence, int64 offset) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ if (offset < 0)
+ return -1;
+
+ LARGE_INTEGER distance, res;
+ distance.QuadPart = offset;
+ DWORD move_method = static_cast<DWORD>(whence);
+ if (!SetFilePointerEx(file_, distance, &res, move_method))
+ return -1;
+ return res.QuadPart;
+}
+
+int File::Read(int64 offset, char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ DCHECK(!async_);
+ if (size < 0)
+ return -1;
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_read;
+ if (::ReadFile(file_, data, size, &bytes_read, &overlapped) != 0)
+ return bytes_read;
+ if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int File::ReadAtCurrentPos(char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ DCHECK(!async_);
+ if (size < 0)
+ return -1;
+
+ DWORD bytes_read;
+ if (::ReadFile(file_, data, size, &bytes_read, NULL) != 0)
+ return bytes_read;
+ if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
+}
+
+int File::ReadNoBestEffort(int64 offset, char* data, int size) {
+ return Read(offset, data, size);
+}
+
+int File::ReadAtCurrentPosNoBestEffort(char* data, int size) {
+ return ReadAtCurrentPos(data, size);
+}
+
+int File::Write(int64 offset, const char* data, int size) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ DCHECK(!async_);
+
+ LARGE_INTEGER offset_li;
+ offset_li.QuadPart = offset;
+
+ OVERLAPPED overlapped = {0};
+ overlapped.Offset = offset_li.LowPart;
+ overlapped.OffsetHigh = offset_li.HighPart;
+
+ DWORD bytes_written;
+ if (::WriteFile(file_, data, size, &bytes_written, &overlapped) != 0)
+ return bytes_written;
+
+ return -1;
+}
+
+int File::WriteAtCurrentPos(const char* data, int size) {
+ NOTREACHED();
+ return -1;
+}
+
+int File::WriteAtCurrentPosNoBestEffort(const char* data, int size) {
+ return WriteAtCurrentPos(data, size);
+}
+
+bool File::Truncate(int64 length) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+
+ // Get the current file pointer.
+ LARGE_INTEGER file_pointer;
+ LARGE_INTEGER zero;
+ zero.QuadPart = 0;
+ if (::SetFilePointerEx(file_, zero, &file_pointer, FILE_CURRENT) == 0)
+ return false;
+
+ LARGE_INTEGER length_li;
+ length_li.QuadPart = length;
+ // If length > file size, SetFilePointerEx() should extend the file
+ // with zeroes on all Windows standard file systems (NTFS, FATxx).
+ if (!::SetFilePointerEx(file_, length_li, NULL, FILE_BEGIN))
+ return false;
+
+ // Set the new file length and move the file pointer to its old position.
+ // This is consistent with ftruncate()'s behavior, even when the file
+ // pointer points to a location beyond the end of the file.
+ return ((::SetEndOfFile(file_) != 0) &&
+ (::SetFilePointerEx(file_, file_pointer, NULL, FILE_BEGIN) != 0));
+}
+
+bool File::Flush() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+ return ::FlushFileBuffers(file_) != FALSE;
+}
+
+bool File::SetTimes(Time last_access_time, Time last_modified_time) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+
+ FILETIME last_access_filetime = last_access_time.ToFileTime();
+ FILETIME last_modified_filetime = last_modified_time.ToFileTime();
+ return (::SetFileTime(file_, NULL, &last_access_filetime,
+ &last_modified_filetime) != 0);
+}
+
+bool File::GetInfo(Info* info) {
+ base::ThreadRestrictions::AssertIOAllowed();
+ DCHECK(IsValid());
+
+ BY_HANDLE_FILE_INFORMATION file_info;
+ if (GetFileInformationByHandle(file_, &file_info) == 0)
+ return false;
+
+ LARGE_INTEGER size;
+ size.HighPart = file_info.nFileSizeHigh;
+ size.LowPart = file_info.nFileSizeLow;
+ info->size = size.QuadPart;
+ info->is_directory =
+ (file_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ info->is_symbolic_link = false; // Windows doesn't have symbolic links.
+ info->last_modified = base::Time::FromFileTime(file_info.ftLastWriteTime);
+ info->last_accessed = base::Time::FromFileTime(file_info.ftLastAccessTime);
+ info->creation_time = base::Time::FromFileTime(file_info.ftCreationTime);
+ return true;
+}
+
+File::Error base::File::Lock() {
+ DCHECK(IsValid());
+ BOOL result = LockFile(file_, 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return OSErrorToFileError(GetLastError());
+ return FILE_OK;
+}
+
+File::Error File::Unlock() {
+ DCHECK(IsValid());
+ BOOL result = UnlockFile(file_, 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return OSErrorToFileError(GetLastError());
+ return FILE_OK;
+}
+
+// Static.
+File::Error File::OSErrorToFileError(DWORD last_error) {
+ switch (last_error) {
+ case ERROR_SHARING_VIOLATION:
+ return FILE_ERROR_IN_USE;
+ case ERROR_FILE_EXISTS:
+ return FILE_ERROR_EXISTS;
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ return FILE_ERROR_NOT_FOUND;
+ case ERROR_ACCESS_DENIED:
+ return FILE_ERROR_ACCESS_DENIED;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return FILE_ERROR_TOO_MANY_OPENED;
+ case ERROR_OUTOFMEMORY:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ return FILE_ERROR_NO_MEMORY;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+ case ERROR_DISK_RESOURCES_EXHAUSTED:
+ return FILE_ERROR_NO_SPACE;
+ case ERROR_USER_MAPPED_FILE:
+ return FILE_ERROR_INVALID_OPERATION;
+ case ERROR_NOT_READY:
+ case ERROR_SECTOR_NOT_FOUND:
+ case ERROR_DEV_NOT_EXIST:
+ case ERROR_IO_DEVICE:
+ case ERROR_FILE_CORRUPT:
+ case ERROR_DISK_CORRUPT:
+ return FILE_ERROR_IO;
+ default:
+ UMA_HISTOGRAM_SPARSE_SLOWLY("PlatformFile.UnknownErrors.Windows",
+ last_error);
+ return FILE_ERROR_FAILED;
+ }
+}
+
+void File::SetPlatformFile(PlatformFile file) {
+ file_.Set(file);
+}
+
+} // namespace base
diff --git a/chromium/base/files/important_file_writer.cc b/chromium/base/files/important_file_writer.cc
index d88498506b4..261c98772e5 100644
--- a/chromium/base/files/important_file_writer.cc
+++ b/chromium/base/files/important_file_writer.cc
@@ -2,6 +2,12 @@
// 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>
@@ -52,7 +58,7 @@ bool ImportantFileWriter::WriteFileAtomically(const FilePath& path,
// as target file, so it can be moved in one step, and that the temp file
// is securely created.
FilePath tmp_file_path;
- if (!file_util::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
+ if (!base::CreateTemporaryFileInDir(path.DirName(), &tmp_file_path)) {
LogFailure(path, FAILED_CREATING, "could not create temporary file");
return false;
}
diff --git a/chromium/base/files/memory_mapped_file_posix.cc b/chromium/base/files/memory_mapped_file_posix.cc
index ba00946722c..c4c477a3fe7 100644
--- a/chromium/base/files/memory_mapped_file_posix.cc
+++ b/chromium/base/files/memory_mapped_file_posix.cc
@@ -9,7 +9,6 @@
#include <unistd.h>
#include "base/logging.h"
-#include "base/posix/eintr_wrapper.h"
#include "base/threading/thread_restrictions.h"
namespace base {
@@ -44,7 +43,7 @@ void MemoryMappedFile::CloseHandles() {
if (data_ != NULL)
munmap(data_, length_);
if (file_ != kInvalidPlatformFileValue)
- ignore_result(HANDLE_EINTR(close(file_)));
+ close(file_);
data_ = NULL;
length_ = 0;
diff --git a/chromium/base/files/scoped_temp_dir.cc b/chromium/base/files/scoped_temp_dir.cc
index 497799e9f72..b893b02ca8a 100644
--- a/chromium/base/files/scoped_temp_dir.cc
+++ b/chromium/base/files/scoped_temp_dir.cc
@@ -23,8 +23,7 @@ bool ScopedTempDir::CreateUniqueTempDir() {
// This "scoped_dir" prefix is only used on Windows and serves as a template
// for the unique name.
- if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_dir"),
- &path_))
+ if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_dir"), &path_))
return false;
return true;
@@ -35,14 +34,13 @@ bool ScopedTempDir::CreateUniqueTempDirUnderPath(const FilePath& base_path) {
return false;
// If |base_path| does not exist, create it.
- if (!file_util::CreateDirectory(base_path))
+ if (!base::CreateDirectory(base_path))
return false;
// Create a new, uniquely named directory under |base_path|.
- if (!file_util::CreateTemporaryDirInDir(
- base_path,
- FILE_PATH_LITERAL("scoped_dir_"),
- &path_))
+ if (!base::CreateTemporaryDirInDir(base_path,
+ FILE_PATH_LITERAL("scoped_dir_"),
+ &path_))
return false;
return true;
@@ -52,7 +50,7 @@ bool ScopedTempDir::Set(const FilePath& path) {
if (!path_.empty())
return false;
- if (!DirectoryExists(path) && !file_util::CreateDirectory(path))
+ if (!DirectoryExists(path) && !base::CreateDirectory(path))
return false;
path_ = path;
diff --git a/chromium/base/files/scoped_temp_dir_unittest.cc b/chromium/base/files/scoped_temp_dir_unittest.cc
index 0c9131c3a8d..fe243ce2ee5 100644
--- a/chromium/base/files/scoped_temp_dir_unittest.cc
+++ b/chromium/base/files/scoped_temp_dir_unittest.cc
@@ -13,8 +13,8 @@ namespace base {
TEST(ScopedTempDir, FullPath) {
FilePath test_path;
- file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"),
- &test_path);
+ base::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"),
+ &test_path);
// Against an existing dir, it should get destroyed when leaving scope.
EXPECT_TRUE(DirectoryExists(test_path));
@@ -55,7 +55,7 @@ TEST(ScopedTempDir, TempDir) {
test_path = dir.path();
EXPECT_TRUE(DirectoryExists(test_path));
FilePath tmp_dir;
- EXPECT_TRUE(file_util::GetTempDir(&tmp_dir));
+ EXPECT_TRUE(base::GetTempDir(&tmp_dir));
EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos);
}
EXPECT_FALSE(DirectoryExists(test_path));
@@ -64,8 +64,8 @@ TEST(ScopedTempDir, TempDir) {
TEST(ScopedTempDir, UniqueTempDirUnderPath) {
// Create a path which will contain a unique temp path.
FilePath base_path;
- ASSERT_TRUE(file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"),
- &base_path));
+ ASSERT_TRUE(base::CreateNewTempDirectory(FILE_PATH_LITERAL("base_dir"),
+ &base_path));
FilePath test_path;
{
diff --git a/chromium/base/guid.cc b/chromium/base/guid.cc
index 657383fc242..b7d79f22790 100644
--- a/chromium/base/guid.cc
+++ b/chromium/base/guid.cc
@@ -4,9 +4,6 @@
#include "base/guid.h"
-#include "base/rand_util.h"
-#include "base/strings/stringprintf.h"
-
namespace base {
bool IsValidGUID(const std::string& guid) {
@@ -14,7 +11,7 @@ bool IsValidGUID(const std::string& guid) {
if (guid.length() != kGUIDLength)
return false;
- std::string hexchars = "0123456789ABCDEF";
+ const std::string hexchars = "0123456789ABCDEF";
for (uint32 i = 0; i < guid.length(); ++i) {
char current = guid[i];
if (i == 8 || i == 13 || i == 18 || i == 23) {
@@ -29,4 +26,4 @@ bool IsValidGUID(const std::string& guid) {
return true;
}
-} // namespace guid
+} // namespace base
diff --git a/chromium/base/guid.h b/chromium/base/guid.h
index 468b65ff619..2a4ed4c4b5c 100644
--- a/chromium/base/guid.h
+++ b/chromium/base/guid.h
@@ -27,6 +27,6 @@ BASE_EXPORT bool IsValidGUID(const std::string& guid);
BASE_EXPORT std::string RandomDataToGUIDString(const uint64 bytes[2]);
#endif
-} // namespace guid
+} // namespace base
#endif // BASE_GUID_H_
diff --git a/chromium/base/i18n/break_iterator.h b/chromium/base/i18n/break_iterator.h
index 96bdeaa9b34..618a320924e 100644
--- a/chromium/base/i18n/break_iterator.h
+++ b/chromium/base/i18n/break_iterator.h
@@ -36,7 +36,7 @@
// name (BREAK_SPACE) implied.
//
// Under BREAK_NEWLINE mode, all characters are included in the returned
-// string, breking only when a newline-equivalent character is encountered
+// string, breaking only when a newline-equivalent character is encountered
// (eg. in the UTF-16 equivalent of the string "foo\nbar!\n\n", the line
// breaks are at the periods in ".foo\n.bar\n.\n.").
//
@@ -85,11 +85,11 @@ class BASE_I18N_EXPORT BreakIterator {
// Under BREAK_WORD mode, returns true if the break we just hit is the
// end of a word. (Otherwise, the break iterator just skipped over e.g.
// whitespace or punctuation.) Under BREAK_LINE and BREAK_NEWLINE modes,
- // this distinction doesn't apply and it always retuns false.
+ // this distinction doesn't apply and it always returns false.
bool IsWord() const;
// Under BREAK_WORD mode, returns true if |position| is at the end of word or
- // at the start of word. It always retuns false under BREAK_LINE and
+ // at the start of word. It always returns false under BREAK_LINE and
// BREAK_NEWLINE modes.
bool IsEndOfWord(size_t position) const;
bool IsStartOfWord(size_t position) const;
diff --git a/chromium/base/i18n/case_conversion_unittest.cc b/chromium/base/i18n/case_conversion_unittest.cc
index 2139bbe7b38..38e2c687822 100644
--- a/chromium/base/i18n/case_conversion_unittest.cc
+++ b/chromium/base/i18n/case_conversion_unittest.cc
@@ -6,6 +6,7 @@
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace base {
namespace {
// Test upper and lower case string conversion.
@@ -24,3 +25,4 @@ TEST(CaseConversionTest, UpperLower) {
// TODO(jshin): More tests are needed, especially with non-ASCII characters.
} // namespace
+} // namespace base
diff --git a/chromium/base/i18n/file_util_icu.cc b/chromium/base/i18n/file_util_icu.cc
index 4b2ca3ac0b2..9b0525086d0 100644
--- a/chromium/base/i18n/file_util_icu.cc
+++ b/chromium/base/i18n/file_util_icu.cc
@@ -19,6 +19,8 @@
#include "third_party/icu/source/common/unicode/uniset.h"
#include "third_party/icu/source/i18n/unicode/coll.h"
+using base::string16;
+
namespace {
class IllegalCharacters {
diff --git a/chromium/base/i18n/file_util_icu.h b/chromium/base/i18n/file_util_icu.h
index 7f246c7f1c7..15526c3a645 100644
--- a/chromium/base/i18n/file_util_icu.h
+++ b/chromium/base/i18n/file_util_icu.h
@@ -15,7 +15,7 @@ namespace file_util {
// Returns true if file_name does not have any illegal character. The input
// param has the same restriction as that for ReplaceIllegalCharacters.
-BASE_I18N_EXPORT bool IsFilenameLegal(const string16& file_name);
+BASE_I18N_EXPORT bool IsFilenameLegal(const base::string16& file_name);
// Replaces characters in 'file_name' that are illegal for file names with
// 'replace_char'. 'file_name' must not be a full or relative path, but just the
diff --git a/chromium/base/i18n/icu_util.cc b/chromium/base/i18n/icu_util.cc
index 3e1353b499d..e5c698475e4 100644
--- a/chromium/base/i18n/icu_util.cc
+++ b/chromium/base/i18n/icu_util.cc
@@ -29,18 +29,6 @@
#define ICU_UTIL_DATA_SHARED 1
#define ICU_UTIL_DATA_STATIC 2
-#ifndef ICU_UTIL_DATA_IMPL
-
-#if defined(OS_WIN)
-#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_SHARED
-#elif defined(OS_IOS)
-#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_FILE
-#else
-#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_STATIC
-#endif
-
-#endif // ICU_UTIL_DATA_IMPL
-
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat"
#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
@@ -86,26 +74,12 @@ bool InitializeICU() {
udata_setCommonData(reinterpret_cast<void*>(addr), &err);
return err == U_ZERO_ERROR;
#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
- // Mac/Linux bundle the ICU data in.
+ // The ICU data is statically linked.
return true;
#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
-#if !defined(OS_MACOSX)
- // For now, expect the data file to be alongside the executable.
- // This is sufficient while we work on unit tests, but will eventually
- // likely live in a data directory.
- FilePath data_path;
- bool path_ok = PathService::Get(base::DIR_EXE, &data_path);
- DCHECK(path_ok);
- u_setDataDirectory(data_path.value().c_str());
- // Only look for the packaged data file;
- // the default behavior is to look for individual files.
- UErrorCode err = U_ZERO_ERROR;
- udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
- return err == U_ZERO_ERROR;
-#else
// If the ICU data directory is set, ICU won't actually load the data until
// it is needed. This can fail if the process is sandboxed at that time.
- // Instead, Mac maps the file in and hands off the data so the sandbox won't
+ // Instead, we map the file in and hand off the data so the sandbox won't
// cause any problems.
// Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever
@@ -113,12 +87,22 @@ bool InitializeICU() {
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)
+ // For now, expect the data file to be alongside the executable.
+ // This is sufficient while we work on unit tests, but will eventually
+ // likely live in a data directory.
+ FilePath data_path;
+ bool path_ok = PathService::Get(base::DIR_EXE, &data_path);
+ DCHECK(path_ok);
+ data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME);
+#else
FilePath data_path =
base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME));
if (data_path.empty()) {
DLOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle";
return false;
}
+#endif // OS check
if (!mapped_file.Initialize(data_path)) {
DLOG(ERROR) << "Couldn't mmap " << data_path.value();
return false;
@@ -127,7 +111,6 @@ bool InitializeICU() {
UErrorCode err = U_ZERO_ERROR;
udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err);
return err == U_ZERO_ERROR;
-#endif // OS check
#endif
}
diff --git a/chromium/base/i18n/time_formatting.cc b/chromium/base/i18n/time_formatting.cc
index 3973dd2508f..917ba43b9ec 100644
--- a/chromium/base/i18n/time_formatting.cc
+++ b/chromium/base/i18n/time_formatting.cc
@@ -12,8 +12,7 @@
#include "third_party/icu/source/i18n/unicode/dtptngen.h"
#include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
-using base::Time;
-
+namespace base {
namespace {
string16 TimeFormat(const icu::DateFormat* formatter,
@@ -48,8 +47,6 @@ string16 TimeFormatWithoutAmPm(const icu::DateFormat* formatter,
} // namespace
-namespace base {
-
string16 TimeFormatTimeOfDay(const Time& time) {
// We can omit the locale parameter because the default should match
// Chrome's application locale.
diff --git a/chromium/base/i18n/timezone.cc b/chromium/base/i18n/timezone.cc
new file mode 100644
index 00000000000..8c652799dbb
--- /dev/null
+++ b/chromium/base/i18n/timezone.cc
@@ -0,0 +1,608 @@
+// 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/i18n/timezone.h"
+
+#include <map>
+
+#include "base/memory/singleton.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "third_party/icu/source/i18n/unicode/timezone.h"
+
+namespace base {
+
+namespace {
+
+class TimezoneMap {
+ public:
+ static TimezoneMap* GetInstance() {
+ return Singleton<TimezoneMap>::get();
+ }
+
+ std::string CountryCodeForTimezone(const std::string& olson_code) {
+ std::map<std::string, std::string>::iterator iter = map_.find(olson_code);
+ if (iter != map_.end())
+ return iter->second;
+
+ return std::string();
+ }
+
+ private:
+ TimezoneMap() {
+ // These mappings are adapted from zone.tab, which is available at
+ // <http://www.ietf.org/timezones/data/zone.tab> and is a part of public
+ // domain.
+ struct OlsonCodeData {
+ std::string country_code;
+ std::string olson_code;
+ } olson_code_data[] = {
+ { "AD", "Europe/Andorra" },
+ { "AE", "Asia/Dubai" },
+ { "AF", "Asia/Kabul" },
+ { "AG", "America/Antigua" },
+ { "AI", "America/Anguilla" },
+ { "AL", "Europe/Tirane" },
+ { "AM", "Asia/Yerevan" },
+ { "AO", "Africa/Luanda" },
+ { "AQ", "Antarctica/McMurdo" },
+ { "AQ", "Antarctica/Rothera" },
+ { "AQ", "Antarctica/Palmer" },
+ { "AQ", "Antarctica/Mawson" },
+ { "AQ", "Antarctica/Davis" },
+ { "AQ", "Antarctica/Casey" },
+ { "AQ", "Antarctica/Vostok" },
+ { "AQ", "Antarctica/DumontDUrville" },
+ { "AQ", "Antarctica/Syowa" },
+ { "AR", "America/Argentina/Buenos_Aires" },
+ { "AR", "America/Argentina/Cordoba" },
+ { "AR", "America/Argentina/Salta" },
+ { "AR", "America/Argentina/Jujuy" },
+ { "AR", "America/Argentina/Tucuman" },
+ { "AR", "America/Argentina/Catamarca" },
+ { "AR", "America/Argentina/La_Rioja" },
+ { "AR", "America/Argentina/San_Juan" },
+ { "AR", "America/Argentina/Mendoza" },
+ { "AR", "America/Argentina/San_Luis" },
+ { "AR", "America/Argentina/Rio_Gallegos" },
+ { "AR", "America/Argentina/Ushuaia" },
+ { "AS", "Pacific/Pago_Pago" },
+ { "AT", "Europe/Vienna" },
+ { "AU", "Australia/Lord_Howe" },
+ { "AU", "Antarctica/Macquarie" },
+ { "AU", "Australia/Hobart" },
+ { "AU", "Australia/Currie" },
+ { "AU", "Australia/Melbourne" },
+ { "AU", "Australia/Sydney" },
+ { "AU", "Australia/Broken_Hill" },
+ { "AU", "Australia/Brisbane" },
+ { "AU", "Australia/Lindeman" },
+ { "AU", "Australia/Adelaide" },
+ { "AU", "Australia/Darwin" },
+ { "AU", "Australia/Perth" },
+ { "AU", "Australia/Eucla" },
+ { "AW", "America/Aruba" },
+ { "AX", "Europe/Mariehamn" },
+ { "AZ", "Asia/Baku" },
+ { "BA", "Europe/Sarajevo" },
+ { "BB", "America/Barbados" },
+ { "BD", "Asia/Dhaka" },
+ { "BE", "Europe/Brussels" },
+ { "BF", "Africa/Ouagadougou" },
+ { "BG", "Europe/Sofia" },
+ { "BH", "Asia/Bahrain" },
+ { "BI", "Africa/Bujumbura" },
+ { "BJ", "Africa/Porto-Novo" },
+ { "BL", "America/St_Barthelemy" },
+ { "BM", "Atlantic/Bermuda" },
+ { "BN", "Asia/Brunei" },
+ { "BO", "America/La_Paz" },
+ { "BQ", "America/Kralendijk" },
+ { "BR", "America/Noronha" },
+ { "BR", "America/Belem" },
+ { "BR", "America/Fortaleza" },
+ { "BR", "America/Recife" },
+ { "BR", "America/Araguaina" },
+ { "BR", "America/Maceio" },
+ { "BR", "America/Bahia" },
+ { "BR", "America/Sao_Paulo" },
+ { "BR", "America/Campo_Grande" },
+ { "BR", "America/Cuiaba" },
+ { "BR", "America/Santarem" },
+ { "BR", "America/Porto_Velho" },
+ { "BR", "America/Boa_Vista" },
+ { "BR", "America/Manaus" },
+ { "BR", "America/Eirunepe" },
+ { "BR", "America/Rio_Branco" },
+ { "BS", "America/Nassau" },
+ { "BT", "Asia/Thimphu" },
+ { "BW", "Africa/Gaborone" },
+ { "BY", "Europe/Minsk" },
+ { "BZ", "America/Belize" },
+ { "CA", "America/St_Johns" },
+ { "CA", "America/Halifax" },
+ { "CA", "America/Glace_Bay" },
+ { "CA", "America/Moncton" },
+ { "CA", "America/Goose_Bay" },
+ { "CA", "America/Blanc-Sablon" },
+ { "CA", "America/Toronto" },
+ { "CA", "America/Nipigon" },
+ { "CA", "America/Thunder_Bay" },
+ { "CA", "America/Iqaluit" },
+ { "CA", "America/Pangnirtung" },
+ { "CA", "America/Resolute" },
+ { "CA", "America/Atikokan" },
+ { "CA", "America/Rankin_Inlet" },
+ { "CA", "America/Winnipeg" },
+ { "CA", "America/Rainy_River" },
+ { "CA", "America/Regina" },
+ { "CA", "America/Swift_Current" },
+ { "CA", "America/Edmonton" },
+ { "CA", "America/Cambridge_Bay" },
+ { "CA", "America/Yellowknife" },
+ { "CA", "America/Inuvik" },
+ { "CA", "America/Creston" },
+ { "CA", "America/Dawson_Creek" },
+ { "CA", "America/Vancouver" },
+ { "CA", "America/Whitehorse" },
+ { "CA", "America/Dawson" },
+ { "CC", "Indian/Cocos" },
+ { "CD", "Africa/Kinshasa" },
+ { "CD", "Africa/Lubumbashi" },
+ { "CF", "Africa/Bangui" },
+ { "CG", "Africa/Brazzaville" },
+ { "CH", "Europe/Zurich" },
+ { "CI", "Africa/Abidjan" },
+ { "CK", "Pacific/Rarotonga" },
+ { "CL", "America/Santiago" },
+ { "CL", "Pacific/Easter" },
+ { "CM", "Africa/Douala" },
+ { "CN", "Asia/Shanghai" },
+ { "CN", "Asia/Harbin" },
+ { "CN", "Asia/Chongqing" },
+ { "CN", "Asia/Urumqi" },
+ { "CN", "Asia/Kashgar" },
+ { "CO", "America/Bogota" },
+ { "CR", "America/Costa_Rica" },
+ { "CU", "America/Havana" },
+ { "CV", "Atlantic/Cape_Verde" },
+ { "CW", "America/Curacao" },
+ { "CX", "Indian/Christmas" },
+ { "CY", "Asia/Nicosia" },
+ { "CZ", "Europe/Prague" },
+ { "DE", "Europe/Berlin" },
+ { "DE", "Europe/Busingen" },
+ { "DJ", "Africa/Djibouti" },
+ { "DK", "Europe/Copenhagen" },
+ { "DM", "America/Dominica" },
+ { "DO", "America/Santo_Domingo" },
+ { "DZ", "Africa/Algiers" },
+ { "EC", "America/Guayaquil" },
+ { "EC", "Pacific/Galapagos" },
+ { "EE", "Europe/Tallinn" },
+ { "EG", "Africa/Cairo" },
+ { "EH", "Africa/El_Aaiun" },
+ { "ER", "Africa/Asmara" },
+ { "ES", "Europe/Madrid" },
+ { "ES", "Africa/Ceuta" },
+ { "ES", "Atlantic/Canary" },
+ { "ET", "Africa/Addis_Ababa" },
+ { "FI", "Europe/Helsinki" },
+ { "FJ", "Pacific/Fiji" },
+ { "FK", "Atlantic/Stanley" },
+ { "FM", "Pacific/Chuuk" },
+ { "FM", "Pacific/Pohnpei" },
+ { "FM", "Pacific/Kosrae" },
+ { "FO", "Atlantic/Faroe" },
+ { "FR", "Europe/Paris" },
+ { "GA", "Africa/Libreville" },
+ { "GB", "Europe/London" },
+ { "GD", "America/Grenada" },
+ { "GE", "Asia/Tbilisi" },
+ { "GF", "America/Cayenne" },
+ { "GG", "Europe/Guernsey" },
+ { "GH", "Africa/Accra" },
+ { "GI", "Europe/Gibraltar" },
+ { "GL", "America/Godthab" },
+ { "GL", "America/Danmarkshavn" },
+ { "GL", "America/Scoresbysund" },
+ { "GL", "America/Thule" },
+ { "GM", "Africa/Banjul" },
+ { "GN", "Africa/Conakry" },
+ { "GP", "America/Guadeloupe" },
+ { "GQ", "Africa/Malabo" },
+ { "GR", "Europe/Athens" },
+ { "GS", "Atlantic/South_Georgia" },
+ { "GT", "America/Guatemala" },
+ { "GU", "Pacific/Guam" },
+ { "GW", "Africa/Bissau" },
+ { "GY", "America/Guyana" },
+ { "HK", "Asia/Hong_Kong" },
+ { "HN", "America/Tegucigalpa" },
+ { "HR", "Europe/Zagreb" },
+ { "HT", "America/Port-au-Prince" },
+ { "HU", "Europe/Budapest" },
+ { "ID", "Asia/Jakarta" },
+ { "ID", "Asia/Pontianak" },
+ { "ID", "Asia/Makassar" },
+ { "ID", "Asia/Jayapura" },
+ { "IE", "Europe/Dublin" },
+ { "IL", "Asia/Jerusalem" },
+ { "IM", "Europe/Isle_of_Man" },
+ { "IN", "Asia/Kolkata" },
+ { "IO", "Indian/Chagos" },
+ { "IQ", "Asia/Baghdad" },
+ { "IR", "Asia/Tehran" },
+ { "IS", "Atlantic/Reykjavik" },
+ { "IT", "Europe/Rome" },
+ { "JE", "Europe/Jersey" },
+ { "JM", "America/Jamaica" },
+ { "JO", "Asia/Amman" },
+ { "JP", "Asia/Tokyo" },
+ { "KE", "Africa/Nairobi" },
+ { "KG", "Asia/Bishkek" },
+ { "KH", "Asia/Phnom_Penh" },
+ { "KI", "Pacific/Tarawa" },
+ { "KI", "Pacific/Enderbury" },
+ { "KI", "Pacific/Kiritimati" },
+ { "KM", "Indian/Comoro" },
+ { "KN", "America/St_Kitts" },
+ { "KP", "Asia/Pyongyang" },
+ { "KR", "Asia/Seoul" },
+ { "KW", "Asia/Kuwait" },
+ { "KY", "America/Cayman" },
+ { "KZ", "Asia/Almaty" },
+ { "KZ", "Asia/Qyzylorda" },
+ { "KZ", "Asia/Aqtobe" },
+ { "KZ", "Asia/Aqtau" },
+ { "KZ", "Asia/Oral" },
+ { "LA", "Asia/Vientiane" },
+ { "LB", "Asia/Beirut" },
+ { "LC", "America/St_Lucia" },
+ { "LI", "Europe/Vaduz" },
+ { "LK", "Asia/Colombo" },
+ { "LR", "Africa/Monrovia" },
+ { "LS", "Africa/Maseru" },
+ { "LT", "Europe/Vilnius" },
+ { "LU", "Europe/Luxembourg" },
+ { "LV", "Europe/Riga" },
+ { "LY", "Africa/Tripoli" },
+ { "MA", "Africa/Casablanca" },
+ { "MC", "Europe/Monaco" },
+ { "MD", "Europe/Chisinau" },
+ { "ME", "Europe/Podgorica" },
+ { "MF", "America/Marigot" },
+ { "MG", "Indian/Antananarivo" },
+ { "MH", "Pacific/Majuro" },
+ { "MH", "Pacific/Kwajalein" },
+ { "MK", "Europe/Skopje" },
+ { "ML", "Africa/Bamako" },
+ { "MM", "Asia/Rangoon" },
+ { "MN", "Asia/Ulaanbaatar" },
+ { "MN", "Asia/Hovd" },
+ { "MN", "Asia/Choibalsan" },
+ { "MO", "Asia/Macau" },
+ { "MP", "Pacific/Saipan" },
+ { "MQ", "America/Martinique" },
+ { "MR", "Africa/Nouakchott" },
+ { "MS", "America/Montserrat" },
+ { "MT", "Europe/Malta" },
+ { "MU", "Indian/Mauritius" },
+ { "MV", "Indian/Maldives" },
+ { "MW", "Africa/Blantyre" },
+ { "MX", "America/Mexico_City" },
+ { "MX", "America/Cancun" },
+ { "MX", "America/Merida" },
+ { "MX", "America/Monterrey" },
+ { "MX", "America/Matamoros" },
+ { "MX", "America/Mazatlan" },
+ { "MX", "America/Chihuahua" },
+ { "MX", "America/Ojinaga" },
+ { "MX", "America/Hermosillo" },
+ { "MX", "America/Tijuana" },
+ { "MX", "America/Santa_Isabel" },
+ { "MX", "America/Bahia_Banderas" },
+ { "MY", "Asia/Kuala_Lumpur" },
+ { "MY", "Asia/Kuching" },
+ { "MZ", "Africa/Maputo" },
+ { "NA", "Africa/Windhoek" },
+ { "NC", "Pacific/Noumea" },
+ { "NE", "Africa/Niamey" },
+ { "NF", "Pacific/Norfolk" },
+ { "NG", "Africa/Lagos" },
+ { "NI", "America/Managua" },
+ { "NL", "Europe/Amsterdam" },
+ { "NO", "Europe/Oslo" },
+ { "NP", "Asia/Kathmandu" },
+ { "NR", "Pacific/Nauru" },
+ { "NU", "Pacific/Niue" },
+ { "NZ", "Pacific/Auckland" },
+ { "NZ", "Pacific/Chatham" },
+ { "OM", "Asia/Muscat" },
+ { "PA", "America/Panama" },
+ { "PE", "America/Lima" },
+ { "PF", "Pacific/Tahiti" },
+ { "PF", "Pacific/Marquesas" },
+ { "PF", "Pacific/Gambier" },
+ { "PG", "Pacific/Port_Moresby" },
+ { "PH", "Asia/Manila" },
+ { "PK", "Asia/Karachi" },
+ { "PL", "Europe/Warsaw" },
+ { "PM", "America/Miquelon" },
+ { "PN", "Pacific/Pitcairn" },
+ { "PR", "America/Puerto_Rico" },
+ { "PS", "Asia/Gaza" },
+ { "PS", "Asia/Hebron" },
+ { "PT", "Europe/Lisbon" },
+ { "PT", "Atlantic/Madeira" },
+ { "PT", "Atlantic/Azores" },
+ { "PW", "Pacific/Palau" },
+ { "PY", "America/Asuncion" },
+ { "QA", "Asia/Qatar" },
+ { "RE", "Indian/Reunion" },
+ { "RO", "Europe/Bucharest" },
+ { "RS", "Europe/Belgrade" },
+ { "RU", "Europe/Kaliningrad" },
+ { "RU", "Europe/Moscow" },
+ { "RU", "Europe/Volgograd" },
+ { "RU", "Europe/Samara" },
+ { "RU", "Asia/Yekaterinburg" },
+ { "RU", "Asia/Omsk" },
+ { "RU", "Asia/Novosibirsk" },
+ { "RU", "Asia/Novokuznetsk" },
+ { "RU", "Asia/Krasnoyarsk" },
+ { "RU", "Asia/Irkutsk" },
+ { "RU", "Asia/Yakutsk" },
+ { "RU", "Asia/Khandyga" },
+ { "RU", "Asia/Vladivostok" },
+ { "RU", "Asia/Sakhalin" },
+ { "RU", "Asia/Ust-Nera" },
+ { "RU", "Asia/Magadan" },
+ { "RU", "Asia/Kamchatka" },
+ { "RU", "Asia/Anadyr" },
+ { "RW", "Africa/Kigali" },
+ { "SA", "Asia/Riyadh" },
+ { "SB", "Pacific/Guadalcanal" },
+ { "SC", "Indian/Mahe" },
+ { "SD", "Africa/Khartoum" },
+ { "SE", "Europe/Stockholm" },
+ { "SG", "Asia/Singapore" },
+ { "SH", "Atlantic/St_Helena" },
+ { "SI", "Europe/Ljubljana" },
+ { "SJ", "Arctic/Longyearbyen" },
+ { "SK", "Europe/Bratislava" },
+ { "SL", "Africa/Freetown" },
+ { "SM", "Europe/San_Marino" },
+ { "SN", "Africa/Dakar" },
+ { "SO", "Africa/Mogadishu" },
+ { "SR", "America/Paramaribo" },
+ { "SS", "Africa/Juba" },
+ { "ST", "Africa/Sao_Tome" },
+ { "SV", "America/El_Salvador" },
+ { "SX", "America/Lower_Princes" },
+ { "SY", "Asia/Damascus" },
+ { "SZ", "Africa/Mbabane" },
+ { "TC", "America/Grand_Turk" },
+ { "TD", "Africa/Ndjamena" },
+ { "TF", "Indian/Kerguelen" },
+ { "TG", "Africa/Lome" },
+ { "TH", "Asia/Bangkok" },
+ { "TJ", "Asia/Dushanbe" },
+ { "TK", "Pacific/Fakaofo" },
+ { "TL", "Asia/Dili" },
+ { "TM", "Asia/Ashgabat" },
+ { "TN", "Africa/Tunis" },
+ { "TO", "Pacific/Tongatapu" },
+ { "TR", "Europe/Istanbul" },
+ { "TT", "America/Port_of_Spain" },
+ { "TV", "Pacific/Funafuti" },
+ { "TW", "Asia/Taipei" },
+ { "TZ", "Africa/Dar_es_Salaam" },
+ { "UA", "Europe/Kiev" },
+ { "UA", "Europe/Uzhgorod" },
+ { "UA", "Europe/Zaporozhye" },
+ { "UA", "Europe/Simferopol" },
+ { "UG", "Africa/Kampala" },
+ { "UM", "Pacific/Johnston" },
+ { "UM", "Pacific/Midway" },
+ { "UM", "Pacific/Wake" },
+ { "US", "America/New_York" },
+ { "US", "America/Detroit" },
+ { "US", "America/Kentucky/Louisville" },
+ { "US", "America/Kentucky/Monticello" },
+ { "US", "America/Indiana/Indianapolis" },
+ { "US", "America/Indiana/Vincennes" },
+ { "US", "America/Indiana/Winamac" },
+ { "US", "America/Indiana/Marengo" },
+ { "US", "America/Indiana/Petersburg" },
+ { "US", "America/Indiana/Vevay" },
+ { "US", "America/Chicago" },
+ { "US", "America/Indiana/Tell_City" },
+ { "US", "America/Indiana/Knox" },
+ { "US", "America/Menominee" },
+ { "US", "America/North_Dakota/Center" },
+ { "US", "America/North_Dakota/New_Salem" },
+ { "US", "America/North_Dakota/Beulah" },
+ { "US", "America/Denver" },
+ { "US", "America/Boise" },
+ { "US", "America/Phoenix" },
+ { "US", "America/Los_Angeles" },
+ { "US", "America/Anchorage" },
+ { "US", "America/Juneau" },
+ { "US", "America/Sitka" },
+ { "US", "America/Yakutat" },
+ { "US", "America/Nome" },
+ { "US", "America/Adak" },
+ { "US", "America/Metlakatla" },
+ { "US", "Pacific/Honolulu" },
+ { "UY", "America/Montevideo" },
+ { "UZ", "Asia/Samarkand" },
+ { "UZ", "Asia/Tashkent" },
+ { "VA", "Europe/Vatican" },
+ { "VC", "America/St_Vincent" },
+ { "VE", "America/Caracas" },
+ { "VG", "America/Tortola" },
+ { "VI", "America/St_Thomas" },
+ { "VN", "Asia/Ho_Chi_Minh" },
+ { "VU", "Pacific/Efate" },
+ { "WF", "Pacific/Wallis" },
+ { "WS", "Pacific/Apia" },
+ { "YE", "Asia/Aden" },
+ { "YT", "Indian/Mayotte" },
+ { "ZA", "Africa/Johannesburg" },
+ { "ZM", "Africa/Lusaka" },
+ { "ZW", "Africa/Harare" },
+ // The mappings below are custom additions to zone.tab.
+ { "GB", "Etc/GMT" },
+ { "GB", "Etc/UTC" },
+ { "GB", "Etc/UCT" },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(olson_code_data); ++i) {
+ map_[olson_code_data[i].olson_code] = olson_code_data[i].country_code;
+ }
+
+ // These are mapping from old codenames to new codenames. They are also
+ // part of public domain, and available at
+ // <http://www.ietf.org/timezones/data/backward>.
+ struct LinkData {
+ std::string old_code;
+ std::string new_code;
+ } link_data[] = {
+ { "Africa/Asmera", "Africa/Asmara" },
+ { "Africa/Timbuktu", "Africa/Bamako" },
+ { "America/Argentina/ComodRivadavia", "America/Argentina/Catamarca" },
+ { "America/Atka", "America/Adak" },
+ { "America/Buenos_Aires", "America/Argentina/Buenos_Aires" },
+ { "America/Catamarca", "America/Argentina/Catamarca" },
+ { "America/Coral_Harbour", "America/Atikokan" },
+ { "America/Cordoba", "America/Argentina/Cordoba" },
+ { "America/Ensenada", "America/Tijuana" },
+ { "America/Fort_Wayne", "America/Indiana/Indianapolis" },
+ { "America/Indianapolis", "America/Indiana/Indianapolis" },
+ { "America/Jujuy", "America/Argentina/Jujuy" },
+ { "America/Knox_IN", "America/Indiana/Knox" },
+ { "America/Louisville", "America/Kentucky/Louisville" },
+ { "America/Mendoza", "America/Argentina/Mendoza" },
+ { "America/Porto_Acre", "America/Rio_Branco" },
+ { "America/Rosario", "America/Argentina/Cordoba" },
+ { "America/Virgin", "America/St_Thomas" },
+ { "Asia/Ashkhabad", "Asia/Ashgabat" },
+ { "Asia/Chungking", "Asia/Chongqing" },
+ { "Asia/Dacca", "Asia/Dhaka" },
+ { "Asia/Katmandu", "Asia/Kathmandu" },
+ { "Asia/Calcutta", "Asia/Kolkata" },
+ { "Asia/Macao", "Asia/Macau" },
+ { "Asia/Tel_Aviv", "Asia/Jerusalem" },
+ { "Asia/Saigon", "Asia/Ho_Chi_Minh" },
+ { "Asia/Thimbu", "Asia/Thimphu" },
+ { "Asia/Ujung_Pandang", "Asia/Makassar" },
+ { "Asia/Ulan_Bator", "Asia/Ulaanbaatar" },
+ { "Atlantic/Faeroe", "Atlantic/Faroe" },
+ { "Atlantic/Jan_Mayen", "Europe/Oslo" },
+ { "Australia/ACT", "Australia/Sydney" },
+ { "Australia/Canberra", "Australia/Sydney" },
+ { "Australia/LHI", "Australia/Lord_Howe" },
+ { "Australia/NSW", "Australia/Sydney" },
+ { "Australia/North", "Australia/Darwin" },
+ { "Australia/Queensland", "Australia/Brisbane" },
+ { "Australia/South", "Australia/Adelaide" },
+ { "Australia/Tasmania", "Australia/Hobart" },
+ { "Australia/Victoria", "Australia/Melbourne" },
+ { "Australia/West", "Australia/Perth" },
+ { "Australia/Yancowinna", "Australia/Broken_Hill" },
+ { "Brazil/Acre", "America/Rio_Branco" },
+ { "Brazil/DeNoronha", "America/Noronha" },
+ { "Brazil/East", "America/Sao_Paulo" },
+ { "Brazil/West", "America/Manaus" },
+ { "Canada/Atlantic", "America/Halifax" },
+ { "Canada/Central", "America/Winnipeg" },
+ { "Canada/East-Saskatchewan", "America/Regina" },
+ { "Canada/Eastern", "America/Toronto" },
+ { "Canada/Mountain", "America/Edmonton" },
+ { "Canada/Newfoundland", "America/St_Johns" },
+ { "Canada/Pacific", "America/Vancouver" },
+ { "Canada/Saskatchewan", "America/Regina" },
+ { "Canada/Yukon", "America/Whitehorse" },
+ { "Chile/Continental", "America/Santiago" },
+ { "Chile/EasterIsland", "Pacific/Easter" },
+ { "Cuba", "America/Havana" },
+ { "Egypt", "Africa/Cairo" },
+ { "Eire", "Europe/Dublin" },
+ { "Europe/Belfast", "Europe/London" },
+ { "Europe/Tiraspol", "Europe/Chisinau" },
+ { "GB", "Europe/London" },
+ { "GB-Eire", "Europe/London" },
+ { "GMT+0", "Etc/GMT" },
+ { "GMT-0", "Etc/GMT" },
+ { "GMT0", "Etc/GMT" },
+ { "Greenwich", "Etc/GMT" },
+ { "Hongkong", "Asia/Hong_Kong" },
+ { "Iceland", "Atlantic/Reykjavik" },
+ { "Iran", "Asia/Tehran" },
+ { "Israel", "Asia/Jerusalem" },
+ { "Jamaica", "America/Jamaica" },
+ { "Japan", "Asia/Tokyo" },
+ { "Kwajalein", "Pacific/Kwajalein" },
+ { "Libya", "Africa/Tripoli" },
+ { "Mexico/BajaNorte", "America/Tijuana" },
+ { "Mexico/BajaSur", "America/Mazatlan" },
+ { "Mexico/General", "America/Mexico_City" },
+ { "NZ", "Pacific/Auckland" },
+ { "NZ-CHAT", "Pacific/Chatham" },
+ { "Navajo", "America/Denver" },
+ { "PRC", "Asia/Shanghai" },
+ { "Pacific/Samoa", "Pacific/Pago_Pago" },
+ { "Pacific/Yap", "Pacific/Chuuk" },
+ { "Pacific/Truk", "Pacific/Chuuk" },
+ { "Pacific/Ponape", "Pacific/Pohnpei" },
+ { "Poland", "Europe/Warsaw" },
+ { "Portugal", "Europe/Lisbon" },
+ { "ROC", "Asia/Taipei" },
+ { "ROK", "Asia/Seoul" },
+ { "Singapore", "Asia/Singapore" },
+ { "Turkey", "Europe/Istanbul" },
+ { "UCT", "Etc/UCT" },
+ { "US/Alaska", "America/Anchorage" },
+ { "US/Aleutian", "America/Adak" },
+ { "US/Arizona", "America/Phoenix" },
+ { "US/Central", "America/Chicago" },
+ { "US/East-Indiana", "America/Indiana/Indianapolis" },
+ { "US/Eastern", "America/New_York" },
+ { "US/Hawaii", "Pacific/Honolulu" },
+ { "US/Indiana-Starke", "America/Indiana/Knox" },
+ { "US/Michigan", "America/Detroit" },
+ { "US/Mountain", "America/Denver" },
+ { "US/Pacific", "America/Los_Angeles" },
+ { "US/Samoa", "Pacific/Pago_Pago" },
+ { "UTC", "Etc/UTC" },
+ { "Universal", "Etc/UTC" },
+ { "W-SU", "Europe/Moscow" },
+ { "Zulu", "Etc/UTC" },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(link_data); ++i) {
+ map_[link_data[i].old_code] = map_[link_data[i].new_code];
+ }
+ }
+
+ friend struct DefaultSingletonTraits<TimezoneMap>;
+
+ std::map<std::string, std::string> map_;
+
+ DISALLOW_COPY_AND_ASSIGN(TimezoneMap);
+};
+
+} // namespace
+
+std::string CountryCodeForCurrentTimezone() {
+ scoped_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
+ icu::UnicodeString id;
+ zone->getID(id);
+ string16 olson_code(id.getBuffer(), id.length());
+ return TimezoneMap::GetInstance()->CountryCodeForTimezone(
+ UTF16ToUTF8(olson_code));
+}
+
+} // namespace base
diff --git a/chromium/base/i18n/timezone.h b/chromium/base/i18n/timezone.h
new file mode 100644
index 00000000000..b7275f7b00b
--- /dev/null
+++ b/chromium/base/i18n/timezone.h
@@ -0,0 +1,21 @@
+// 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_I18N_TIMEZONE_H_
+#define BASE_I18N_TIMEZONE_H_
+
+#include <string>
+
+#include "base/i18n/base_i18n_export.h"
+
+namespace base {
+
+// Checks the system timezone and turns it into a two-character ASCII country
+// code. This may fail (for example, it will always fail on Android), in which
+// case it will return an empty string.
+BASE_I18N_EXPORT std::string CountryCodeForCurrentTimezone();
+
+} // namespace base
+
+#endif // BASE_TIME_TIMEZONE_H_
diff --git a/chromium/base/i18n/timezone_unittest.cc b/chromium/base/i18n/timezone_unittest.cc
new file mode 100644
index 00000000000..2cdcc422985
--- /dev/null
+++ b/chromium/base/i18n/timezone_unittest.cc
@@ -0,0 +1,21 @@
+// 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/i18n/timezone.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace {
+
+TEST(TimezoneTest, CountryCodeForCurrentTimezone) {
+ std::string country_code = CountryCodeForCurrentTimezone();
+ // On some systems (such as Android or some flavors of Linux), icu may come up
+ // empty.
+ if (!country_code.empty())
+ EXPECT_EQ(2U, country_code.size());
+}
+
+} // namespace
+} // namespace base
diff --git a/chromium/base/ios/device_util.h b/chromium/base/ios/device_util.h
index fe4833dd362..a4fa4887d35 100644
--- a/chromium/base/ios/device_util.h
+++ b/chromium/base/ios/device_util.h
@@ -40,7 +40,7 @@ namespace device_util {
// x86_64 -> Simulator
std::string GetPlatform();
-// Returns true if the application is running on a high-ram device. (>=250M).
+// Returns true if the application is running on a high-ram device. (>=500M).
bool IsRunningOnHighRamDevice();
// Returns true if the device has only one core.
diff --git a/chromium/base/ios/device_util.mm b/chromium/base/ios/device_util.mm
index 96008332bef..0d516f9a2b4 100644
--- a/chromium/base/ios/device_util.mm
+++ b/chromium/base/ios/device_util.mm
@@ -72,8 +72,8 @@ bool IsRunningOnHighRamDevice() {
uint64_t memory_size = 0;
size_t size = sizeof(memory_size);
if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) {
- // Anything >= 250M, call high ram.
- return memory_size >= 250 * 1024 * 1024;
+ // Anything >= 500M, call high ram.
+ return memory_size >= 500 * 1024 * 1024;
}
return false;
}
diff --git a/chromium/base/ios/scoped_critical_action.h b/chromium/base/ios/scoped_critical_action.h
index 660a83ad024..803d5875176 100644
--- a/chromium/base/ios/scoped_critical_action.h
+++ b/chromium/base/ios/scoped_critical_action.h
@@ -5,6 +5,7 @@
#ifndef BASE_IOS_SCOPED_CRITICAL_ACTION_H_
#define BASE_IOS_SCOPED_CRITICAL_ACTION_H_
+#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
namespace base {
@@ -29,15 +30,34 @@ class ScopedCriticalAction {
~ScopedCriticalAction();
private:
- // Informs the OS that the background task has completed.
- void EndBackgroundTask();
-
- // |UIBackgroundTaskIdentifier| returned by
- // |beginBackgroundTaskWithExpirationHandler:| when marking the beginning of
- // a long-running background task. It is defined as an |unsigned int| instead
- // of a |UIBackgroundTaskIdentifier| so this class can be used in .cc files.
- unsigned int background_task_id_;
- Lock background_task_id_lock_;
+ // Core logic; ScopedCriticalAction should not be reference counted so
+ // that it follows the normal pattern of stack-allocating ScopedFoo objects,
+ // but the expiration handler needs to have a reference counted object to
+ // refer to.
+ class Core : public base::RefCountedThreadSafe<Core> {
+ public:
+ Core();
+
+ // Informs the OS that the background task has completed.
+ void EndBackgroundTask();
+
+ private:
+ friend base::RefCountedThreadSafe<Core>;
+ ~Core();
+
+ // |UIBackgroundTaskIdentifier| returned by
+ // |beginBackgroundTaskWithExpirationHandler:| when marking the beginning of
+ // a long-running background task. It is defined as an |unsigned int|
+ // instead of a |UIBackgroundTaskIdentifier| so this class can be used in
+ // .cc files.
+ unsigned int background_task_id_;
+ Lock background_task_id_lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+ };
+
+ // The instance of the core that drives the background task.
+ scoped_refptr<Core> core_;
DISALLOW_COPY_AND_ASSIGN(ScopedCriticalAction);
};
diff --git a/chromium/base/ios/scoped_critical_action.mm b/chromium/base/ios/scoped_critical_action.mm
index 734c0a215b6..9dad70ed6ba 100644
--- a/chromium/base/ios/scoped_critical_action.mm
+++ b/chromium/base/ios/scoped_critical_action.mm
@@ -7,22 +7,32 @@
#import <UIKit/UIKit.h>
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
namespace base {
namespace ios {
+ScopedCriticalAction::ScopedCriticalAction()
+ : core_(new ScopedCriticalAction::Core()) {
+}
+
+ScopedCriticalAction::~ScopedCriticalAction() {
+ core_->EndBackgroundTask();
+}
+
// This implementation calls |beginBackgroundTaskWithExpirationHandler:| when
// instantiated and |endBackgroundTask:| when destroyed, creating a scope whose
// execution will continue (temporarily) even after the app is backgrounded.
-ScopedCriticalAction::ScopedCriticalAction() {
+ScopedCriticalAction::Core::Core() {
+ scoped_refptr<ScopedCriticalAction::Core> core = this;
background_task_id_ = [[UIApplication sharedApplication]
beginBackgroundTaskWithExpirationHandler:^{
DLOG(WARNING) << "Background task with id " << background_task_id_
<< " expired.";
// Note if |endBackgroundTask:| is not called for each task before time
// expires, the system kills the application.
- EndBackgroundTask();
+ core->EndBackgroundTask();
}];
if (background_task_id_ == UIBackgroundTaskInvalid) {
DLOG(WARNING) <<
@@ -32,11 +42,11 @@ ScopedCriticalAction::ScopedCriticalAction() {
}
}
-ScopedCriticalAction::~ScopedCriticalAction() {
- EndBackgroundTask();
+ScopedCriticalAction::Core::~Core() {
+ DCHECK_EQ(background_task_id_, UIBackgroundTaskInvalid);
}
-void ScopedCriticalAction::EndBackgroundTask() {
+void ScopedCriticalAction::Core::EndBackgroundTask() {
UIBackgroundTaskIdentifier task_id;
{
AutoLock lock_scope(background_task_id_lock_);
diff --git a/chromium/base/json/json_reader.cc b/chromium/base/json/json_reader.cc
index 593273ebd44..cbf6c99eda5 100644
--- a/chromium/base/json/json_reader.cc
+++ b/chromium/base/json/json_reader.cc
@@ -9,6 +9,10 @@
namespace base {
+// Values 1000 and above are used by JSONFileValueSerializer::JsonFileError.
+COMPILE_ASSERT(JSONReader::JSON_PARSE_ERROR_COUNT < 1000,
+ json_reader_error_out_of_bounds);
+
const char* JSONReader::kInvalidEscape =
"Invalid escape sequence.";
const char* JSONReader::kSyntaxError =
diff --git a/chromium/base/json/json_reader.h b/chromium/base/json/json_reader.h
index e6028462788..9b306878e7f 100644
--- a/chromium/base/json/json_reader.h
+++ b/chromium/base/json/json_reader.h
@@ -73,6 +73,7 @@ class BASE_EXPORT JSONReader {
JSON_UNEXPECTED_DATA_AFTER_ROOT,
JSON_UNSUPPORTED_ENCODING,
JSON_UNQUOTED_DICTIONARY_KEY,
+ JSON_PARSE_ERROR_COUNT
};
// String versions of parse error codes.
diff --git a/chromium/base/json/json_string_value_serializer.h b/chromium/base/json/json_string_value_serializer.h
index 8aa3f95bee5..014ef9f7c57 100644
--- a/chromium/base/json/json_string_value_serializer.h
+++ b/chromium/base/json/json_string_value_serializer.h
@@ -47,7 +47,7 @@ class BASE_EXPORT JSONStringValueSerializer : public base::ValueSerializer {
// Attempt to deserialize the data structure encoded in the string passed
// in to the constructor into a structure of Value objects. If the return
// value is NULL, and if |error_code| is non-null, |error_code| will
- // contain an integer error code (either JsonFileError or JsonParseError).
+ // contain an integer error code (a JsonParseError in this case).
// If |error_message| is non-null, it will be filled in with a formatted
// error message including the location of the error if appropriate.
// The caller takes ownership of the returned value.
diff --git a/chromium/base/json/json_value_serializer_unittest.cc b/chromium/base/json/json_value_serializer_unittest.cc
index 314cd07a5b8..44c0a57cd27 100644
--- a/chromium/base/json/json_value_serializer_unittest.cc
+++ b/chromium/base/json/json_value_serializer_unittest.cc
@@ -235,22 +235,23 @@ TEST(JSONValueSerializerTest, StringEscape) {
std::string all_chars_expected =
"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000B\\f\\r"
"\\u000E\\u000F\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017"
- "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F !\\\""
- "#$%&'()*+,-./0123456789:;\\u003C=\\u003E?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\"
- "\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\u007F\\u0080\\u0081\\u0082\\u0083"
- "\\u0084\\u0085\\u0086\\u0087\\u0088\\u0089\\u008A\\u008B\\u008C\\u008D"
- "\\u008E\\u008F\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097"
- "\\u0098\\u0099\\u009A\\u009B\\u009C\\u009D\\u009E\\u009F\\u00A0\\u00A1"
- "\\u00A2\\u00A3\\u00A4\\u00A5\\u00A6\\u00A7\\u00A8\\u00A9\\u00AA\\u00AB"
- "\\u00AC\\u00AD\\u00AE\\u00AF\\u00B0\\u00B1\\u00B2\\u00B3\\u00B4\\u00B5"
- "\\u00B6\\u00B7\\u00B8\\u00B9\\u00BA\\u00BB\\u00BC\\u00BD\\u00BE\\u00BF"
- "\\u00C0\\u00C1\\u00C2\\u00C3\\u00C4\\u00C5\\u00C6\\u00C7\\u00C8\\u00C9"
- "\\u00CA\\u00CB\\u00CC\\u00CD\\u00CE\\u00CF\\u00D0\\u00D1\\u00D2\\u00D3"
- "\\u00D4\\u00D5\\u00D6\\u00D7\\u00D8\\u00D9\\u00DA\\u00DB\\u00DC\\u00DD"
- "\\u00DE\\u00DF\\u00E0\\u00E1\\u00E2\\u00E3\\u00E4\\u00E5\\u00E6\\u00E7"
- "\\u00E8\\u00E9\\u00EA\\u00EB\\u00EC\\u00ED\\u00EE\\u00EF\\u00F0\\u00F1"
- "\\u00F2\\u00F3\\u00F4\\u00F5\\u00F6\\u00F7\\u00F8\\u00F9\\u00FA\\u00FB"
- "\\u00FC\\u00FD\\u00FE\\u00FF";
+ "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F !\\\"#$%&'()*+,"
+ "-./0123456789:;\\u003C=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcde"
+ "fghijklmnopqrstuvwxyz{|}~\x7F\xC2\x80\xC2\x81\xC2\x82\xC2\x83\xC2\x84"
+ "\xC2\x85\xC2\x86\xC2\x87\xC2\x88\xC2\x89\xC2\x8A\xC2\x8B\xC2\x8C\xC2\x8D"
+ "\xC2\x8E\xC2\x8F\xC2\x90\xC2\x91\xC2\x92\xC2\x93\xC2\x94\xC2\x95\xC2\x96"
+ "\xC2\x97\xC2\x98\xC2\x99\xC2\x9A\xC2\x9B\xC2\x9C\xC2\x9D\xC2\x9E\xC2\x9F"
+ "\xC2\xA0\xC2\xA1\xC2\xA2\xC2\xA3\xC2\xA4\xC2\xA5\xC2\xA6\xC2\xA7\xC2\xA8"
+ "\xC2\xA9\xC2\xAA\xC2\xAB\xC2\xAC\xC2\xAD\xC2\xAE\xC2\xAF\xC2\xB0\xC2\xB1"
+ "\xC2\xB2\xC2\xB3\xC2\xB4\xC2\xB5\xC2\xB6\xC2\xB7\xC2\xB8\xC2\xB9\xC2\xBA"
+ "\xC2\xBB\xC2\xBC\xC2\xBD\xC2\xBE\xC2\xBF\xC3\x80\xC3\x81\xC3\x82\xC3\x83"
+ "\xC3\x84\xC3\x85\xC3\x86\xC3\x87\xC3\x88\xC3\x89\xC3\x8A\xC3\x8B\xC3\x8C"
+ "\xC3\x8D\xC3\x8E\xC3\x8F\xC3\x90\xC3\x91\xC3\x92\xC3\x93\xC3\x94\xC3\x95"
+ "\xC3\x96\xC3\x97\xC3\x98\xC3\x99\xC3\x9A\xC3\x9B\xC3\x9C\xC3\x9D\xC3\x9E"
+ "\xC3\x9F\xC3\xA0\xC3\xA1\xC3\xA2\xC3\xA3\xC3\xA4\xC3\xA5\xC3\xA6\xC3\xA7"
+ "\xC3\xA8\xC3\xA9\xC3\xAA\xC3\xAB\xC3\xAC\xC3\xAD\xC3\xAE\xC3\xAF\xC3\xB0"
+ "\xC3\xB1\xC3\xB2\xC3\xB3\xC3\xB4\xC3\xB5\xC3\xB6\xC3\xB7\xC3\xB8\xC3\xB9"
+ "\xC3\xBA\xC3\xBB\xC3\xBC\xC3\xBD\xC3\xBE\xC3\xBF";
std::string expected_output = "{\"all_chars\":\"" + all_chars_expected +
"\"}";
@@ -273,7 +274,7 @@ TEST(JSONValueSerializerTest, UnicodeStrings) {
string16 test(WideToUTF16(L"\x7F51\x9875"));
root.SetString("web", test);
- std::string expected = "{\"web\":\"\\u7F51\\u9875\"}";
+ std::string expected = "{\"web\":\"\xE7\xBD\x91\xE9\xA1\xB5\"}";
std::string actual;
JSONStringValueSerializer serializer(&actual);
diff --git a/chromium/base/json/json_writer.cc b/chromium/base/json/json_writer.cc
index 6a9cc6aa470..d6006638219 100644
--- a/chromium/base/json/json_writer.cc
+++ b/chromium/base/json/json_writer.cc
@@ -21,28 +21,24 @@ static const char kPrettyPrintLineEnding[] = "\r\n";
static const char kPrettyPrintLineEnding[] = "\n";
#endif
-/* static */
-const char* JSONWriter::kEmptyArray = "[]";
-
-/* static */
+// static
void JSONWriter::Write(const Value* const node, std::string* json) {
WriteWithOptions(node, 0, json);
}
-/* static */
+// static
void JSONWriter::WriteWithOptions(const Value* const node, int options,
std::string* json) {
json->clear();
// Is there a better way to estimate the size of the output?
json->reserve(1024);
- bool escape = !(options & OPTIONS_DO_NOT_ESCAPE);
bool omit_binary_values = !!(options & OPTIONS_OMIT_BINARY_VALUES);
bool omit_double_type_preservation =
!!(options & OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION);
bool pretty_print = !!(options & OPTIONS_PRETTY_PRINT);
- JSONWriter writer(escape, omit_binary_values, omit_double_type_preservation,
+ JSONWriter writer(omit_binary_values, omit_double_type_preservation,
pretty_print, json);
writer.BuildJSONString(node, 0);
@@ -50,11 +46,10 @@ void JSONWriter::WriteWithOptions(const Value* const node, int options,
json->append(kPrettyPrintLineEnding);
}
-JSONWriter::JSONWriter(bool escape, bool omit_binary_values,
+JSONWriter::JSONWriter(bool omit_binary_values,
bool omit_double_type_preservation, bool pretty_print,
std::string* json)
- : escape_(escape),
- omit_binary_values_(omit_binary_values),
+ : omit_binary_values_(omit_binary_values),
omit_double_type_preservation_(omit_double_type_preservation),
pretty_print_(pretty_print),
json_string_(json) {
@@ -123,11 +118,7 @@ void JSONWriter::BuildJSONString(const Value* const node, int depth) {
std::string value;
bool result = node->GetAsString(&value);
DCHECK(result);
- if (escape_) {
- JsonDoubleQuote(UTF8ToUTF16(value), true, json_string_);
- } else {
- JsonDoubleQuote(value, true, json_string_);
- }
+ EscapeJSONString(value, true, json_string_);
break;
}
@@ -169,7 +160,7 @@ void JSONWriter::BuildJSONString(const Value* const node, int depth) {
json_string_->append(kPrettyPrintLineEnding);
const DictionaryValue* dict =
- static_cast<const DictionaryValue*>(node);
+ static_cast<const DictionaryValue*>(node);
bool first_entry = true;
for (DictionaryValue::Iterator itr(*dict); !itr.IsAtEnd();
itr.Advance(), first_entry = false) {
@@ -186,7 +177,8 @@ void JSONWriter::BuildJSONString(const Value* const node, int depth) {
if (pretty_print_)
IndentLine(depth + 1);
- AppendQuotedString(itr.key());
+
+ EscapeJSONString(itr.key(), true, json_string_);
if (pretty_print_) {
json_string_->append(": ");
} else {
@@ -218,12 +210,6 @@ void JSONWriter::BuildJSONString(const Value* const node, int depth) {
}
}
-void JSONWriter::AppendQuotedString(const std::string& str) {
- // TODO(viettrungluu): |str| is UTF-8, not ASCII, so to properly escape it we
- // have to convert it to UTF-16. This round-trip is suboptimal.
- JsonDoubleQuote(UTF8ToUTF16(str), true, json_string_);
-}
-
void JSONWriter::IndentLine(int depth) {
// It may be faster to keep an indent string so we don't have to keep
// reallocating.
diff --git a/chromium/base/json/json_writer.h b/chromium/base/json/json_writer.h
index 94052c80343..e4a143c7780 100644
--- a/chromium/base/json/json_writer.h
+++ b/chromium/base/json/json_writer.h
@@ -17,24 +17,19 @@ class Value;
class BASE_EXPORT JSONWriter {
public:
enum Options {
- // Do not escape the string, preserving its UTF8 characters. It is useful
- // if you can pass the resulting string to the JSON parser in binary form
- // (as UTF8).
- OPTIONS_DO_NOT_ESCAPE = 1 << 0,
-
// For values of binary type, the value (and key if within a dictionary)
// will be omitted from the output.
- OPTIONS_OMIT_BINARY_VALUES = 1 << 1,
+ OPTIONS_OMIT_BINARY_VALUES = 1 << 0,
// This option instructs the writer to write doubles that have no fractional
// part as a normal integer (i.e., without using exponential notation
// or appending a '.0') as long as the value is within the range of a
// 64-bit int.
- OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 2,
+ OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1,
// Return a slightly nicer formatted json string (pads with whitespace to
// help with readability).
- OPTIONS_PRETTY_PRINT = 1 << 3
+ OPTIONS_PRETTY_PRINT = 1 << 2,
};
// Given a root node, generates a JSON string and puts it into |json|.
@@ -48,12 +43,8 @@ class BASE_EXPORT JSONWriter {
static void WriteWithOptions(const Value* const node, int options,
std::string* json);
- // A static, constant JSON string representing an empty array. Useful
- // for empty JSON argument passing.
- static const char* kEmptyArray;
-
private:
- JSONWriter(bool escape, bool omit_binary_values,
+ JSONWriter(bool omit_binary_values,
bool omit_double_type_preservation, bool pretty_print,
std::string* json);
@@ -61,13 +52,9 @@ class BASE_EXPORT JSONWriter {
// json_string_ will contain the JSON.
void BuildJSONString(const Value* const node, int depth);
- // Appends a quoted, escaped, version of (UTF-8) str to json_string_.
- void AppendQuotedString(const std::string& str);
-
// Adds space to json_string_ for the indent level.
void IndentLine(int depth);
- bool escape_;
bool omit_binary_values_;
bool omit_double_type_preservation_;
bool pretty_print_;
diff --git a/chromium/base/json/string_escape.cc b/chromium/base/json/string_escape.cc
index 10ea6707465..a3b0735191e 100644
--- a/chromium/base/json/string_escape.cc
+++ b/chromium/base/json/string_escape.cc
@@ -8,40 +8,56 @@
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversion_utils.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/third_party/icu/icu_utf.h"
namespace base {
namespace {
-// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful,
-// returns true and appends the escape sequence to |dst|. This isn't required
-// by the spec, but it's more readable by humans than the \uXXXX alternatives.
-template<typename CHAR>
-static bool JsonSingleEscapeChar(const CHAR c, std::string* dst) {
+// Format string for printing a \uXXXX escape sequence.
+const char kU16EscapeFormat[] = "\\u%04X";
+
+// The code point to output for an invalid input code unit.
+const uint32 kReplacementCodePoint = 0xFFFD;
+
+// Used below in EscapeSpecialCodePoint().
+COMPILE_ASSERT('<' == 0x3C, less_than_sign_is_0x3c);
+
+// Try to escape the |code_point| if it is a known special character. If
+// successful, returns true and appends the escape sequence to |dest|. This
+// isn't required by the spec, but it's more readable by humans.
+bool EscapeSpecialCodePoint(uint32 code_point, std::string* dest) {
// WARNING: if you add a new case here, you need to update the reader as well.
// Note: \v is in the reader, but not here since the JSON spec doesn't
// allow it.
- switch (c) {
+ switch (code_point) {
case '\b':
- dst->append("\\b");
+ dest->append("\\b");
break;
case '\f':
- dst->append("\\f");
+ dest->append("\\f");
break;
case '\n':
- dst->append("\\n");
+ dest->append("\\n");
break;
case '\r':
- dst->append("\\r");
+ dest->append("\\r");
break;
case '\t':
- dst->append("\\t");
+ dest->append("\\t");
break;
case '\\':
- dst->append("\\\\");
+ dest->append("\\\\");
break;
case '"':
- dst->append("\\\"");
+ dest->append("\\\"");
+ break;
+ // Escape < to prevent script execution; escaping > is not necessary and
+ // not doing so save a few bytes.
+ case '<':
+ dest->append("\\u003C");
break;
default:
return false;
@@ -49,57 +65,90 @@ static bool JsonSingleEscapeChar(const CHAR c, std::string* dst) {
return true;
}
-template <class STR>
-void JsonDoubleQuoteT(const STR& str,
- bool put_in_quotes,
- std::string* dst) {
+template <typename S>
+bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) {
+ bool did_replacement = false;
+
if (put_in_quotes)
- dst->push_back('"');
-
- for (typename STR::const_iterator it = str.begin(); it != str.end(); ++it) {
- typename ToUnsigned<typename STR::value_type>::Unsigned c = *it;
- if (!JsonSingleEscapeChar(c, dst)) {
- if (c < 32 || c > 126 || c == '<' || c == '>') {
- // 1. Escaping <, > to prevent script execution.
- // 2. Technically, we could also pass through c > 126 as UTF8, but this
- // is also optional. It would also be a pain to implement here.
- unsigned int as_uint = static_cast<unsigned int>(c);
- base::StringAppendF(dst, "\\u%04X", as_uint);
- } else {
- unsigned char ascii = static_cast<unsigned char>(*it);
- dst->push_back(ascii);
- }
+ dest->push_back('"');
+
+ // Casting is necessary because ICU uses int32. Try and do so safely.
+ CHECK_LE(str.length(), static_cast<size_t>(kint32max));
+ const int32 length = static_cast<int32>(str.length());
+
+ for (int32 i = 0; i < length; ++i) {
+ uint32 code_point;
+ if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point)) {
+ code_point = kReplacementCodePoint;
+ did_replacement = true;
}
+
+ if (EscapeSpecialCodePoint(code_point, dest))
+ continue;
+
+ // Escape non-printing characters.
+ if (code_point < 32)
+ base::StringAppendF(dest, kU16EscapeFormat, code_point);
+ else
+ WriteUnicodeCharacter(code_point, dest);
}
if (put_in_quotes)
- dst->push_back('"');
+ dest->push_back('"');
+
+ return !did_replacement;
}
} // namespace
-void JsonDoubleQuote(const StringPiece& str,
- bool put_in_quotes,
- std::string* dst) {
- JsonDoubleQuoteT(str, put_in_quotes, dst);
+bool EscapeJSONString(const StringPiece& str,
+ bool put_in_quotes,
+ std::string* dest) {
+ return EscapeJSONStringImpl(str, put_in_quotes, dest);
}
-std::string GetDoubleQuotedJson(const StringPiece& str) {
- std::string dst;
- JsonDoubleQuote(str, true, &dst);
- return dst;
+bool EscapeJSONString(const StringPiece16& str,
+ bool put_in_quotes,
+ std::string* dest) {
+ return EscapeJSONStringImpl(str, put_in_quotes, dest);
+}
+
+std::string GetQuotedJSONString(const StringPiece& str) {
+ std::string dest;
+ bool ok = EscapeJSONStringImpl(str, true, &dest);
+ DCHECK(ok);
+ return dest;
}
-void JsonDoubleQuote(const StringPiece16& str,
- bool put_in_quotes,
- std::string* dst) {
- JsonDoubleQuoteT(str, put_in_quotes, dst);
+std::string GetQuotedJSONString(const StringPiece16& str) {
+ std::string dest;
+ bool ok = EscapeJSONStringImpl(str, true, &dest);
+ DCHECK(ok);
+ return dest;
}
-std::string GetDoubleQuotedJson(const StringPiece16& str) {
- std::string dst;
- JsonDoubleQuote(str, true, &dst);
- return dst;
+std::string EscapeBytesAsInvalidJSONString(const StringPiece& str,
+ bool put_in_quotes) {
+ std::string dest;
+
+ if (put_in_quotes)
+ dest.push_back('"');
+
+ for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) {
+ ToUnsigned<StringPiece::value_type>::Unsigned c = *it;
+ if (EscapeSpecialCodePoint(c, &dest))
+ continue;
+
+ if (c < 32 || c > 126)
+ base::StringAppendF(&dest, kU16EscapeFormat, c);
+ else
+ dest.push_back(*it);
+ }
+
+ if (put_in_quotes)
+ dest.push_back('"');
+
+ return dest;
}
} // namespace base
diff --git a/chromium/base/json/string_escape.h b/chromium/base/json/string_escape.h
index 0f16f59d4fd..b66b7e54532 100644
--- a/chromium/base/json/string_escape.h
+++ b/chromium/base/json/string_escape.h
@@ -1,8 +1,8 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-//
-// This file defines utility functions for escaping strings.
+
+// This file defines utility functions for escaping strings suitable for JSON.
#ifndef BASE_JSON_STRING_ESCAPE_H_
#define BASE_JSON_STRING_ESCAPE_H_
@@ -14,24 +14,46 @@
namespace base {
-// Escape |str| appropriately for a JSON string literal, _appending_ the
-// result to |dst|. This will create unicode escape sequences (\uXXXX).
-// If |put_in_quotes| is true, the result will be surrounded in double quotes.
-// The outputted literal, when interpreted by the browser, should result in a
-// javascript string that is identical and the same length as the input |str|.
-BASE_EXPORT void JsonDoubleQuote(const StringPiece& str,
- bool put_in_quotes,
- std::string* dst);
-
-// Same as above, but always returns the result double quoted.
-BASE_EXPORT std::string GetDoubleQuotedJson(const StringPiece& str);
-
-BASE_EXPORT void JsonDoubleQuote(const StringPiece16& str,
- bool put_in_quotes,
- std::string* dst);
-
-// Same as above, but always returns the result double quoted.
-BASE_EXPORT std::string GetDoubleQuotedJson(const StringPiece16& str);
+// Appends to |dest| an escaped version of |str|. Valid UTF-8 code units will
+// pass through from the input to the output. Invalid code units will be
+// replaced with the U+FFFD replacement character. This function returns true
+// if no replacement was necessary and false if there was a lossy replacement.
+// On return, |dest| will contain a valid UTF-8 JSON string.
+//
+// Non-printing control characters will be escaped as \uXXXX sequences for
+// readability.
+//
+// If |put_in_quotes| is true, then a leading and trailing double-quote mark
+// will be appended to |dest| as well.
+BASE_EXPORT bool EscapeJSONString(const StringPiece& str,
+ bool put_in_quotes,
+ std::string* dest);
+
+// Performs a similar function to the UTF-8 StringPiece version above,
+// converting UTF-16 code units to UTF-8 code units and escaping non-printing
+// control characters. On return, |dest| will contain a valid UTF-8 JSON string.
+BASE_EXPORT bool EscapeJSONString(const StringPiece16& str,
+ bool put_in_quotes,
+ std::string* dest);
+
+// Helper functions that wrap the above two functions but return the value
+// instead of appending. |put_in_quotes| is always true.
+BASE_EXPORT std::string GetQuotedJSONString(const StringPiece& str);
+BASE_EXPORT std::string GetQuotedJSONString(const StringPiece16& str);
+
+// Given an arbitrary byte string |str|, this will escape all non-ASCII bytes
+// as \uXXXX escape sequences. This function is *NOT* meant to be used with
+// Unicode strings and does not validate |str| as one.
+//
+// CAVEAT CALLER: The output of this function may not be valid JSON, since
+// JSON requires escape sequences to be valid UTF-16 code units. This output
+// will be mangled if passed to to the base::JSONReader, since the reader will
+// interpret it as UTF-16 and convert it to UTF-8.
+//
+// The output of this function takes the *appearance* of JSON but is not in
+// fact valid according to RFC 4627.
+BASE_EXPORT std::string EscapeBytesAsInvalidJSONString(const StringPiece& str,
+ bool put_in_quotes);
} // namespace base
diff --git a/chromium/base/json/string_escape_unittest.cc b/chromium/base/json/string_escape_unittest.cc
index f9219943744..7d82f9bd9b1 100644
--- a/chromium/base/json/string_escape_unittest.cc
+++ b/chromium/base/json/string_escape_unittest.cc
@@ -1,104 +1,182 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// 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/json/string_escape.h"
+
+#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
-namespace {
-
-const struct json_narrow_test_data {
- const char* to_escape;
- const char* escaped;
-} json_narrow_cases[] = {
- {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
- {"a\b\f\n\r\t\v\1\\.\"z",
- "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
- {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
- {"c<>d", "c\\u003C\\u003Ed"},
-};
-
-} // namespace
-
-TEST(StringEscapeTest, JsonDoubleQuoteNarrow) {
- for (size_t i = 0; i < arraysize(json_narrow_cases); ++i) {
- const char* in_ptr = json_narrow_cases[i].to_escape;
+TEST(JSONStringEscapeTest, EscapeUTF8) {
+ const struct {
+ const char* to_escape;
+ const char* escaped;
+ } cases[] = {
+ {"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+ {"a\b\f\n\r\t\v\1\\.\"z",
+ "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+ {"b\x0f\x7f\xf0\xff!", // \xf0\xff is not a valid UTF-8 unit.
+ "b\\u000F\x7F\xEF\xBF\xBD\xEF\xBF\xBD!"},
+ {"c<>d", "c\\u003C>d"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ const char* in_ptr = cases[i].to_escape;
std::string in_str = in_ptr;
+
std::string out;
- JsonDoubleQuote(in_ptr, false, &out);
- EXPECT_EQ(std::string(json_narrow_cases[i].escaped), out);
+ EscapeJSONString(in_ptr, false, &out);
+ EXPECT_EQ(std::string(cases[i].escaped), out);
+ EXPECT_TRUE(IsStringUTF8(out));
+
out.erase();
- JsonDoubleQuote(in_str, false, &out);
- EXPECT_EQ(std::string(json_narrow_cases[i].escaped), out);
+ bool convert_ok = EscapeJSONString(in_str, false, &out);
+ EXPECT_EQ(std::string(cases[i].escaped), out);
+ EXPECT_TRUE(IsStringUTF8(out));
+
+ if (convert_ok) {
+ std::string fooout = GetQuotedJSONString(in_str);
+ EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", fooout);
+ EXPECT_TRUE(IsStringUTF8(out));
+ }
}
- std::string in = json_narrow_cases[0].to_escape;
+ std::string in = cases[0].to_escape;
std::string out;
- JsonDoubleQuote(in, false, &out);
+ EscapeJSONString(in, false, &out);
+ EXPECT_TRUE(IsStringUTF8(out));
// test quoting
std::string out_quoted;
- JsonDoubleQuote(in, true, &out_quoted);
+ EscapeJSONString(in, true, &out_quoted);
EXPECT_EQ(out.length() + 2, out_quoted.length());
EXPECT_EQ(out_quoted.find(out), 1U);
+ EXPECT_TRUE(IsStringUTF8(out_quoted));
// now try with a NULL in the string
std::string null_prepend = "test";
null_prepend.push_back(0);
in = null_prepend + in;
std::string expected = "test\\u0000";
- expected += json_narrow_cases[0].escaped;
+ expected += cases[0].escaped;
out.clear();
- JsonDoubleQuote(in, false, &out);
+ EscapeJSONString(in, false, &out);
EXPECT_EQ(expected, out);
+ EXPECT_TRUE(IsStringUTF8(out));
}
-namespace {
-
-const struct json_wide_test_data {
- const wchar_t* to_escape;
- const char* escaped;
-} json_wide_cases[] = {
- {L"b\uffb1\u00ff", "b\\uFFB1\\u00FF"},
- {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
- {L"a\b\f\n\r\t\v\1\\.\"z",
- "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
- {L"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
- {L"c<>d", "c\\u003C\\u003Ed"},
-};
+TEST(JSONStringEscapeTest, EscapeUTF16) {
+ const struct {
+ const wchar_t* to_escape;
+ const char* escaped;
+ } cases[] = {
+ {L"b\uffb1\u00ff", "b\xEF\xBE\xB1\xC3\xBF"},
+ {L"\b\001aZ\"\\wee", "\\b\\u0001aZ\\\"\\\\wee"},
+ {L"a\b\f\n\r\t\v\1\\.\"z",
+ "a\\b\\f\\n\\r\\t\\u000B\\u0001\\\\.\\\"z"},
+ {L"b\x0f\x7f\xf0\xff!", "b\\u000F\x7F\xC3\xB0\xC3\xBF!"},
+ {L"c<>d", "c\\u003C>d"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ string16 in = WideToUTF16(cases[i].to_escape);
-} // namespace
-
-TEST(StringEscapeTest, JsonDoubleQuoteWide) {
- for (size_t i = 0; i < arraysize(json_wide_cases); ++i) {
std::string out;
- string16 in = WideToUTF16(json_wide_cases[i].to_escape);
- JsonDoubleQuote(in, false, &out);
- EXPECT_EQ(std::string(json_wide_cases[i].escaped), out);
+ EscapeJSONString(in, false, &out);
+ EXPECT_EQ(std::string(cases[i].escaped), out);
+ EXPECT_TRUE(IsStringUTF8(out));
+
+ out = GetQuotedJSONString(in);
+ EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"", out);
+ EXPECT_TRUE(IsStringUTF8(out));
}
- string16 in = WideToUTF16(json_wide_cases[0].to_escape);
+ string16 in = WideToUTF16(cases[0].to_escape);
std::string out;
- JsonDoubleQuote(in, false, &out);
+ EscapeJSONString(in, false, &out);
+ EXPECT_TRUE(IsStringUTF8(out));
// test quoting
std::string out_quoted;
- JsonDoubleQuote(in, true, &out_quoted);
+ EscapeJSONString(in, true, &out_quoted);
EXPECT_EQ(out.length() + 2, out_quoted.length());
EXPECT_EQ(out_quoted.find(out), 1U);
+ EXPECT_TRUE(IsStringUTF8(out));
// now try with a NULL in the string
string16 null_prepend = WideToUTF16(L"test");
null_prepend.push_back(0);
in = null_prepend + in;
std::string expected = "test\\u0000";
- expected += json_wide_cases[0].escaped;
+ expected += cases[0].escaped;
out.clear();
- JsonDoubleQuote(in, false, &out);
+ EscapeJSONString(in, false, &out);
EXPECT_EQ(expected, out);
+ EXPECT_TRUE(IsStringUTF8(out));
+}
+
+TEST(JSONStringEscapeTest, EscapeUTF16OutsideBMP) {
+ {
+ // {a, U+10300, !}, SMP.
+ string16 test;
+ test.push_back('a');
+ test.push_back(0xD800);
+ test.push_back(0xDF00);
+ test.push_back('!');
+ std::string actual;
+ EXPECT_TRUE(EscapeJSONString(test, false, &actual));
+ EXPECT_EQ("a\xF0\x90\x8C\x80!", actual);
+ }
+ {
+ // {U+20021, U+2002B}, SIP.
+ string16 test;
+ test.push_back(0xD840);
+ test.push_back(0xDC21);
+ test.push_back(0xD840);
+ test.push_back(0xDC2B);
+ std::string actual;
+ EXPECT_TRUE(EscapeJSONString(test, false, &actual));
+ EXPECT_EQ("\xF0\xA0\x80\xA1\xF0\xA0\x80\xAB", actual);
+ }
+ {
+ // {?, U+D800, @}, lone surrogate.
+ string16 test;
+ test.push_back('?');
+ test.push_back(0xD800);
+ test.push_back('@');
+ std::string actual;
+ EXPECT_FALSE(EscapeJSONString(test, false, &actual));
+ EXPECT_EQ("?\xEF\xBF\xBD@", actual);
+ }
+}
+
+TEST(JSONStringEscapeTest, EscapeBytes) {
+ const struct {
+ const char* to_escape;
+ const char* escaped;
+ } cases[] = {
+ {"b\x0f\x7f\xf0\xff!", "b\\u000F\\u007F\\u00F0\\u00FF!"},
+ {"\xe5\xc4\x4f\x05\xb6\xfd\0", "\\u00E5\\u00C4O\\u0005\\u00B6\\u00FD"},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ std::string in = std::string(cases[i].to_escape);
+ EXPECT_FALSE(IsStringUTF8(in));
+
+ EXPECT_EQ(std::string(cases[i].escaped),
+ EscapeBytesAsInvalidJSONString(in, false));
+ EXPECT_EQ("\"" + std::string(cases[i].escaped) + "\"",
+ EscapeBytesAsInvalidJSONString(in, true));
+ }
+
+ const char kEmbedNull[] = { '\xab', '\x39', '\0', '\x9f', '\xab' };
+ std::string in(kEmbedNull, ARRAYSIZE_UNSAFE(kEmbedNull));
+ EXPECT_FALSE(IsStringUTF8(in));
+ EXPECT_EQ(std::string("\\u00AB9\\u0000\\u009F\\u00AB"),
+ EscapeBytesAsInvalidJSONString(in, false));
}
} // namespace base
diff --git a/chromium/base/linux_util.cc b/chromium/base/linux_util.cc
index 5f9ecd43ab1..f8dd2d0088b 100644
--- a/chromium/base/linux_util.cc
+++ b/chromium/base/linux_util.cc
@@ -287,8 +287,7 @@ pid_t FindThreadIDWithSyscall(pid_t pid, const std::string& expected_data,
continue;
if (syscall_supported != NULL)
*syscall_supported = true;
- bool read_ret =
- file_util::ReadFromFD(fd, syscall_data.get(), expected_data.length());
+ bool read_ret = ReadFromFD(fd, syscall_data.get(), expected_data.length());
close(fd);
if (!read_ret)
continue;
diff --git a/chromium/base/logging.h b/chromium/base/logging.h
index 859f2602fc7..71f391f8aff 100644
--- a/chromium/base/logging.h
+++ b/chromium/base/logging.h
@@ -776,7 +776,12 @@ const LogSeverity LOG_DCHECK = LOG_INFO;
#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+#if defined(NDEBUG) && defined(OS_CHROMEOS)
+#define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " << \
+ __FUNCTION__ << ". "
+#else
#define NOTREACHED() DCHECK(false)
+#endif
// Redefine the standard assert to use our nice log files
#undef assert
diff --git a/chromium/base/logging_unittest.cc b/chromium/base/logging_unittest.cc
index bff10ebdf8d..4996abc2240 100644
--- a/chromium/base/logging_unittest.cc
+++ b/chromium/base/logging_unittest.cc
@@ -17,9 +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
void LogSink(const std::string& str) {
++log_sink_call_count;
}
+#endif // !LOGGING_IS_OFFICIAL_BUILD
// Class to make sure any manipulations we do to the min log level are
// contained (i.e., do not affect other unit tests).
@@ -167,7 +169,7 @@ TEST_F(LoggingTest, LoggingIsLazy) {
}
// Official builds have CHECKs directly call BreakDebugger.
-#if !defined(LOGGING_IS_OFFICIAL_BUILD)
+#if !LOGGING_IS_OFFICIAL_BUILD
TEST_F(LoggingTest, CheckStreamsAreLazy) {
MockLogSource mock_log_source, uncalled_mock_log_source;
@@ -202,8 +204,7 @@ TEST_F(LoggingTest, DebugLoggingReleaseBehavior) {
TEST_F(LoggingTest, DcheckStreamsAreLazy) {
MockLogSource mock_log_source;
EXPECT_CALL(mock_log_source, Log()).Times(0);
-#if !defined(LOGGING_IS_OFFICIAL_BUILD) && defined(NDEBUG) && \
- !defined(DCHECK_ALWAYS_ON)
+#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);
DCHECK(mock_log_source.Log()) << mock_log_source.Log();
diff --git a/chromium/base/mac/authorization_util.h b/chromium/base/mac/authorization_util.h
index b34348d175d..4629039c410 100644
--- a/chromium/base/mac/authorization_util.h
+++ b/chromium/base/mac/authorization_util.h
@@ -33,11 +33,20 @@
namespace base {
namespace mac {
-// Obtains an AuthorizationRef that can be used to run commands as root. If
-// necessary, prompts the user for authentication. If the user is prompted,
+// Obtains an AuthorizationRef for the rights indicated by |rights|. If
+// necessary, prompts the user for authentication. If the user is prompted,
// |prompt| will be used as the prompt string and an icon appropriate for the
-// application will be displayed in a prompt dialog. Note that the system
-// appends its own text to the prompt string. Returns NULL on failure.
+// application will be displayed in a prompt dialog. Note that the system
+// appends its own text to the prompt string. |extraFlags| will be ORed
+// together with the default flags. Returns NULL on failure.
+BASE_EXPORT
+AuthorizationRef GetAuthorizationRightsWithPrompt(
+ AuthorizationRights* rights,
+ CFStringRef prompt,
+ AuthorizationFlags extraFlags);
+
+// Obtains an AuthorizationRef (using |GetAuthorizationRightsWithPrompt|) that
+// can be used to run commands as root.
BASE_EXPORT
AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt);
diff --git a/chromium/base/mac/authorization_util.mm b/chromium/base/mac/authorization_util.mm
index c2925896209..6cb8de3f73b 100644
--- a/chromium/base/mac/authorization_util.mm
+++ b/chromium/base/mac/authorization_util.mm
@@ -22,7 +22,10 @@
namespace base {
namespace mac {
-AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
+AuthorizationRef GetAuthorizationRightsWithPrompt(
+ AuthorizationRights* rights,
+ CFStringRef prompt,
+ AuthorizationFlags extraFlags) {
// Create an empty AuthorizationRef.
ScopedAuthorizationRef authorization;
OSStatus status = AuthorizationCreate(NULL,
@@ -34,12 +37,11 @@ AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
return NULL;
}
- // Specify the "system.privilege.admin" right, which allows
- // AuthorizationExecuteWithPrivileges to run commands as root.
- AuthorizationItem right_items[] = {
- {kAuthorizationRightExecute, 0, NULL, 0}
- };
- AuthorizationRights rights = {arraysize(right_items), right_items};
+ AuthorizationFlags flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagExtendRights |
+ kAuthorizationFlagPreAuthorize |
+ extraFlags;
// product_logo_32.png is used instead of app.icns because Authorization
// Services can't deal with .icns files.
@@ -63,16 +65,12 @@ AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
AuthorizationEnvironment environment = {arraysize(environment_items),
environment_items};
- AuthorizationFlags flags = kAuthorizationFlagDefaults |
- kAuthorizationFlagInteractionAllowed |
- kAuthorizationFlagExtendRights |
- kAuthorizationFlagPreAuthorize;
-
status = AuthorizationCopyRights(authorization,
- &rights,
+ rights,
&environment,
flags,
NULL);
+
if (status != errAuthorizationSuccess) {
if (status != errAuthorizationCanceled) {
OSSTATUS_LOG(ERROR, status) << "AuthorizationCopyRights";
@@ -83,6 +81,17 @@ AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
return authorization.release();
}
+AuthorizationRef AuthorizationCreateToRunAsRoot(CFStringRef prompt) {
+ // Specify the "system.privilege.admin" right, which allows
+ // AuthorizationExecuteWithPrivileges to run commands as root.
+ AuthorizationItem right_items[] = {
+ {kAuthorizationRightExecute, 0, NULL, 0}
+ };
+ AuthorizationRights rights = {arraysize(right_items), right_items};
+
+ return GetAuthorizationRightsWithPrompt(&rights, prompt, 0);
+}
+
OSStatus ExecuteWithPrivilegesAndGetPID(AuthorizationRef authorization,
const char* tool_path,
AuthorizationFlags options,
diff --git a/chromium/base/mac/foundation_util.h b/chromium/base/mac/foundation_util.h
index 447f7bf0d89..46ea3c324c3 100644
--- a/chromium/base/mac/foundation_util.h
+++ b/chromium/base/mac/foundation_util.h
@@ -16,10 +16,14 @@
#if defined(__OBJC__)
#import <Foundation/Foundation.h>
+@class NSFont;
+@class UIFont;
#else // __OBJC__
#include <CoreFoundation/CoreFoundation.h>
class NSBundle;
+class NSFont;
class NSString;
+class UIFont;
#endif // __OBJC__
#if defined(OS_IOS)
@@ -225,6 +229,12 @@ CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream);
CF_TO_NS_MUTABLE_CAST_DECL(String);
CF_TO_NS_CAST_DECL(CFURL, NSURL);
+#if defined(OS_IOS)
+CF_TO_NS_CAST_DECL(CTFont, UIFont);
+#else
+CF_TO_NS_CAST_DECL(CTFont, NSFont);
+#endif
+
#undef CF_TO_NS_CAST_DECL
#undef CF_TO_NS_MUTABLE_CAST_DECL
#undef OBJC_CPP_CLASS_DECL
diff --git a/chromium/base/mac/foundation_util.mm b/chromium/base/mac/foundation_util.mm
index 19df847a7ea..4e9b2248874 100644
--- a/chromium/base/mac/foundation_util.mm
+++ b/chromium/base/mac/foundation_util.mm
@@ -17,6 +17,7 @@
extern "C" {
CFTypeID SecACLGetTypeID();
CFTypeID SecTrustedApplicationGetTypeID();
+Boolean _CFIsObjC(CFTypeID typeID, CFTypeRef obj);
} // extern "C"
#endif
@@ -290,6 +291,31 @@ CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream);
CF_TO_NS_MUTABLE_CAST_DEFN(String);
CF_TO_NS_CAST_DEFN(CFURL, NSURL);
+#if defined(OS_IOS)
+CF_TO_NS_CAST_DEFN(CTFont, UIFont);
+#else
+// The NSFont/CTFont toll-free bridging is broken when it comes to type
+// checking, so do some special-casing.
+// http://www.openradar.me/15341349 rdar://15341349
+NSFont* CFToNSCast(CTFontRef cf_val) {
+ NSFont* ns_val =
+ const_cast<NSFont*>(reinterpret_cast<const NSFont*>(cf_val));
+ DCHECK(!cf_val ||
+ CTFontGetTypeID() == CFGetTypeID(cf_val) ||
+ (_CFIsObjC(CTFontGetTypeID(), cf_val) &&
+ [ns_val isKindOfClass:NSClassFromString(@"NSFont")]));
+ return ns_val;
+}
+
+CTFontRef NSToCFCast(NSFont* ns_val) {
+ CTFontRef cf_val = reinterpret_cast<CTFontRef>(ns_val);
+ DCHECK(!cf_val ||
+ CTFontGetTypeID() == CFGetTypeID(cf_val) ||
+ [ns_val isKindOfClass:NSClassFromString(@"NSFont")]);
+ return cf_val;
+}
+#endif
+
#undef CF_TO_NS_CAST_DEFN
#undef CF_TO_NS_MUTABLE_CAST_DEFN
@@ -327,9 +353,41 @@ CF_CAST_DEFN(CFUUID);
CF_CAST_DEFN(CGColor);
-CF_CAST_DEFN(CTFont);
CF_CAST_DEFN(CTRun);
+#if defined(OS_IOS)
+CF_CAST_DEFN(CTFont);
+#else
+// The NSFont/CTFont toll-free bridging is broken when it comes to type
+// checking, so do some special-casing.
+// http://www.openradar.me/15341349 rdar://15341349
+template<> CTFontRef
+CFCast<CTFontRef>(const CFTypeRef& cf_val) {
+ if (cf_val == NULL) {
+ return NULL;
+ }
+ if (CFGetTypeID(cf_val) == CTFontGetTypeID()) {
+ return (CTFontRef)(cf_val);
+ }
+
+ if (!_CFIsObjC(CTFontGetTypeID(), cf_val))
+ return NULL;
+
+ id<NSObject> ns_val = reinterpret_cast<id>(const_cast<void*>(cf_val));
+ if ([ns_val isKindOfClass:NSClassFromString(@"NSFont")]) {
+ return (CTFontRef)(cf_val);
+ }
+ return NULL;
+}
+
+template<> CTFontRef
+CFCastStrict<CTFontRef>(const CFTypeRef& cf_val) {
+ CTFontRef rv = CFCast<CTFontRef>(cf_val);
+ DCHECK(cf_val == NULL || rv);
+ return rv;
+}
+#endif
+
#if !defined(OS_IOS)
CF_CAST_DEFN(SecACL);
CF_CAST_DEFN(SecTrustedApplication);
diff --git a/chromium/base/mac/foundation_util_unittest.mm b/chromium/base/mac/foundation_util_unittest.mm
index fc7dd099c57..3b72b1225a8 100644
--- a/chromium/base/mac/foundation_util_unittest.mm
+++ b/chromium/base/mac/foundation_util_unittest.mm
@@ -308,8 +308,7 @@ TEST(FoundationUtilTest, FilePathToNSString) {
EXPECT_NSEQ(@"/a/b", FilePathToNSString(FilePath("/a/b")));
}
-// http://crbug.com/173983 Fails consistently under Mac ASAN.
-TEST(FoundationUtilTest, DISABLED_NSStringToFilePath) {
+TEST(FoundationUtilTest, NSStringToFilePath) {
EXPECT_EQ(FilePath(), NSStringToFilePath(nil));
EXPECT_EQ(FilePath(), NSStringToFilePath(@""));
EXPECT_EQ(FilePath("/a/b"), NSStringToFilePath(@"/a/b"));
diff --git a/chromium/base/mac/mac_util.h b/chromium/base/mac/mac_util.h
index 0605cbaf2c8..e827f37c9fb 100644
--- a/chromium/base/mac/mac_util.h
+++ b/chromium/base/mac/mac_util.h
@@ -92,13 +92,6 @@ BASE_EXPORT bool AmIForeground();
// Excludes the file given by |file_path| from being backed up by Time Machine.
BASE_EXPORT bool SetFileBackupExclusion(const FilePath& file_path);
-// Converts a NSImage to a CGImageRef. Normally, the system frameworks can do
-// this fine, especially on 10.6. On 10.5, however, CGImage cannot handle
-// converting a PDF-backed NSImage into a CGImageRef. This function will
-// rasterize the PDF into a bitmap CGImage. The caller is responsible for
-// releasing the return value.
-BASE_EXPORT CGImageRef CopyNSImageToCGImage(NSImage* image);
-
// Checks if the current application is set as a Login Item, so it will launch
// on Login. If a non-NULL pointer to is_hidden is passed, the Login Item also
// is queried for the 'hide on launch' flag.
@@ -142,12 +135,22 @@ BASE_EXPORT bool IsOSLionOrLater();
// Mountain Lion is Mac OS X 10.8, Darwin 12.
BASE_EXPORT bool IsOSMountainLion();
+BASE_EXPORT bool IsOSMountainLionOrEarlier();
BASE_EXPORT bool IsOSMountainLionOrLater();
+// Mavericks is Mac OS X 10.9, Darwin 13.
+BASE_EXPORT bool IsOSMavericks();
+BASE_EXPORT bool IsOSMavericksOrLater();
+
// This should be infrequently used. It only makes sense to use this to avoid
// codepaths that are very likely to break on future (unreleased, untested,
// unborn) OS releases, or to log when the OS is newer than any known version.
-BASE_EXPORT bool IsOSLaterThanMountainLion_DontCallThis();
+BASE_EXPORT bool IsOSLaterThanMavericks_DontCallThis();
+
+// Inline functions that are redundant due to version ranges being mutually-
+// exclusive.
+inline bool IsOSLionOrEarlier() { return !IsOSMountainLionOrLater(); }
+inline bool IsOSMountainLionOrEarlier() { return !IsOSMavericksOrLater(); }
// 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
@@ -165,7 +168,6 @@ inline bool IsOSLionOrLater() { return true; }
MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_7
#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_7
inline bool IsOSLion() { return false; }
-inline bool IsOSLionOrEarlier() { return false; }
#endif
#if defined(MAC_OS_X_VERSION_10_8) && \
@@ -178,9 +180,19 @@ inline bool IsOSMountainLionOrLater() { return true; }
MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_8
#define BASE_MAC_MAC_UTIL_H_INLINED_GT_10_8
inline bool IsOSMountainLion() { return false; }
-inline bool IsOSLaterThanMountainLion_DontCallThis() {
- return true;
-}
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_9) && \
+ MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+#define BASE_MAC_MAC_UTIL_H_INLINED_GE_10_9
+inline bool IsOSMavericksOrLater() { return true; }
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_9) && \
+ 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
// 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 89730745c79..c2565991abb 100644
--- a/chromium/base/mac/mac_util.mm
+++ b/chromium/base/mac/mac_util.mm
@@ -298,39 +298,6 @@ bool SetFileBackupExclusion(const FilePath& file_path) {
return os_err == noErr;
}
-// Converts a NSImage to a CGImageRef. Normally, the system frameworks can do
-// this fine, especially on 10.6. On 10.5, however, CGImage cannot handle
-// converting a PDF-backed NSImage into a CGImageRef. This function will
-// rasterize the PDF into a bitmap CGImage. The caller is responsible for
-// releasing the return value.
-CGImageRef CopyNSImageToCGImage(NSImage* image) {
- // This is based loosely on http://www.cocoadev.com/index.pl?CGImageRef .
- NSSize size = [image size];
- ScopedCFTypeRef<CGContextRef> context(
- CGBitmapContextCreate(NULL, // Allow CG to allocate memory.
- size.width,
- size.height,
- 8, // bitsPerComponent
- 0, // bytesPerRow - CG will calculate by default.
- [[NSColorSpace genericRGBColorSpace] CGColorSpace],
- kCGBitmapByteOrder32Host |
- kCGImageAlphaPremultipliedFirst));
- if (!context.get())
- return NULL;
-
- [NSGraphicsContext saveGraphicsState];
- [NSGraphicsContext setCurrentContext:
- [NSGraphicsContext graphicsContextWithGraphicsPort:context.get()
- flipped:NO]];
- [image drawInRect:NSMakeRect(0,0, size.width, size.height)
- fromRect:NSZeroRect
- operation:NSCompositeCopy
- fraction:1.0];
- [NSGraphicsContext restoreGraphicsState];
-
- return CGBitmapContextCreateImage(context);
-}
-
bool CheckLoginItemStatus(bool* is_hidden) {
ScopedCFTypeRef<LSSharedFileListItemRef> item(GetLoginItemForApp());
if (!item.get())
@@ -502,7 +469,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 > 12) << "Assuming Darwin "
+ DLOG_IF(WARNING, darwin_major_version > 13) << "Assuming Darwin "
<< base::IntToString(darwin_major_version) << " is Mac OS X 10."
<< base::IntToString(mac_os_x_minor_version);
@@ -520,6 +487,7 @@ enum {
SNOW_LEOPARD_MINOR_VERSION = 6,
LION_MINOR_VERSION = 7,
MOUNTAIN_LION_MINOR_VERSION = 8,
+ MAVERICKS_MINOR_VERSION = 9,
};
} // namespace
@@ -536,12 +504,6 @@ bool IsOSLion() {
}
#endif
-#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_7)
-bool IsOSLionOrEarlier() {
- return MacOSXMinorVersion() <= LION_MINOR_VERSION;
-}
-#endif
-
#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_7)
bool IsOSLionOrLater() {
return MacOSXMinorVersion() >= LION_MINOR_VERSION;
@@ -560,9 +522,21 @@ bool IsOSMountainLionOrLater() {
}
#endif
-#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_8)
-bool IsOSLaterThanMountainLion_DontCallThis() {
- return MacOSXMinorVersion() > MOUNTAIN_LION_MINOR_VERSION;
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9)
+bool IsOSMavericks() {
+ return MacOSXMinorVersion() == MAVERICKS_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GE_10_9)
+bool IsOSMavericksOrLater() {
+ return MacOSXMinorVersion() >= MAVERICKS_MINOR_VERSION;
+}
+#endif
+
+#if !defined(BASE_MAC_MAC_UTIL_H_INLINED_GT_10_9)
+bool IsOSLaterThanMavericks_DontCallThis() {
+ return MacOSXMinorVersion() > MAVERICKS_MINOR_VERSION;
}
#endif
diff --git a/chromium/base/mac/mac_util_unittest.mm b/chromium/base/mac/mac_util_unittest.mm
index d2467578704..1b56814abec 100644
--- a/chromium/base/mac/mac_util_unittest.mm
+++ b/chromium/base/mac/mac_util_unittest.mm
@@ -124,20 +124,6 @@ TEST_F(MacUtilTest, TestExcludeFileFromBackups) {
EXPECT_FALSE(excluded_by_path);
}
-TEST_F(MacUtilTest, CopyNSImageToCGImage) {
- base::scoped_nsobject<NSImage> nsImage(
- [[NSImage alloc] initWithSize:NSMakeSize(20, 20)]);
- [nsImage lockFocus];
- [[NSColor redColor] set];
- NSRect rect = NSZeroRect;
- rect.size = [nsImage size];
- NSRectFill(rect);
- [nsImage unlockFocus];
-
- ScopedCFTypeRef<CGImageRef> cgImage(CopyNSImageToCGImage(nsImage.get()));
- EXPECT_TRUE(cgImage.get());
-}
-
TEST_F(MacUtilTest, NSObjectRetainRelease) {
base::scoped_nsobject<NSArray> array(
[[NSArray alloc] initWithObjects:@"foo", nil]);
@@ -161,26 +147,46 @@ TEST_F(MacUtilTest, IsOSEllipsis) {
EXPECT_TRUE(IsOSLionOrEarlier());
EXPECT_FALSE(IsOSLionOrLater());
EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_TRUE(IsOSMountainLionOrEarlier());
EXPECT_FALSE(IsOSMountainLionOrLater());
- EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ EXPECT_FALSE(IsOSMavericks());
+ EXPECT_FALSE(IsOSMavericksOrLater());
+ EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
} else if (minor == 7) {
EXPECT_FALSE(IsOSSnowLeopard());
EXPECT_TRUE(IsOSLion());
EXPECT_TRUE(IsOSLionOrEarlier());
EXPECT_TRUE(IsOSLionOrLater());
EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_TRUE(IsOSMountainLionOrEarlier());
EXPECT_FALSE(IsOSMountainLionOrLater());
- EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ EXPECT_FALSE(IsOSMavericks());
+ EXPECT_FALSE(IsOSMavericksOrLater());
+ EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
} else if (minor == 8) {
EXPECT_FALSE(IsOSSnowLeopard());
EXPECT_FALSE(IsOSLion());
EXPECT_FALSE(IsOSLionOrEarlier());
EXPECT_TRUE(IsOSLionOrLater());
EXPECT_TRUE(IsOSMountainLion());
+ EXPECT_TRUE(IsOSMountainLionOrEarlier());
+ EXPECT_TRUE(IsOSMountainLionOrLater());
+ EXPECT_FALSE(IsOSMavericks());
+ EXPECT_FALSE(IsOSMavericksOrLater());
+ EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
+ } else if (minor == 9) {
+ EXPECT_FALSE(IsOSSnowLeopard());
+ EXPECT_FALSE(IsOSLion());
+ EXPECT_FALSE(IsOSLionOrEarlier());
+ EXPECT_TRUE(IsOSLionOrLater());
+ EXPECT_FALSE(IsOSMountainLion());
+ EXPECT_FALSE(IsOSMountainLionOrEarlier());
EXPECT_TRUE(IsOSMountainLionOrLater());
- EXPECT_FALSE(IsOSLaterThanMountainLion_DontCallThis());
+ EXPECT_TRUE(IsOSMavericks());
+ EXPECT_TRUE(IsOSMavericksOrLater());
+ EXPECT_FALSE(IsOSLaterThanMavericks_DontCallThis());
} else {
- // Not five, six, seven, or eight. Ah, ah, ah.
+ // Not five, six, seven, eight, or nine. Ah, ah, ah.
EXPECT_TRUE(false);
}
} else {
@@ -214,7 +220,7 @@ TEST_F(MacUtilTest, TestRemoveQuarantineAttribute) {
ScopedTempDir temp_dir_;
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
FilePath dummy_folder_path = temp_dir_.path().Append("DummyFolder");
- ASSERT_TRUE(file_util::CreateDirectory(dummy_folder_path));
+ ASSERT_TRUE(base::CreateDirectory(dummy_folder_path));
const char* quarantine_str = "0000;4b392bb2;Chromium;|org.chromium.Chromium";
const char* file_path_str = dummy_folder_path.value().c_str();
EXPECT_EQ(0, setxattr(file_path_str, "com.apple.quarantine",
@@ -232,7 +238,7 @@ TEST_F(MacUtilTest, TestRemoveQuarantineAttributeTwice) {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
FilePath dummy_folder_path = temp_dir_.path().Append("DummyFolder");
const char* file_path_str = dummy_folder_path.value().c_str();
- ASSERT_TRUE(file_util::CreateDirectory(dummy_folder_path));
+ ASSERT_TRUE(base::CreateDirectory(dummy_folder_path));
EXPECT_EQ(-1, getxattr(file_path_str, "com.apple.quarantine", NULL, 0, 0, 0));
// No quarantine attribute to begin with, but RemoveQuarantineAttribute still
// succeeds because in the end the folder still doesn't have the quarantine
diff --git a/chromium/base/mac/sdk_forward_declarations.h b/chromium/base/mac/sdk_forward_declarations.h
index a2d4013723d..bbaf962b639 100644
--- a/chromium/base/mac/sdk_forward_declarations.h
+++ b/chromium/base/mac/sdk_forward_declarations.h
@@ -22,6 +22,7 @@ enum {
NSEventPhaseChanged = 0x1 << 2,
NSEventPhaseEnded = 0x1 << 3,
NSEventPhaseCancelled = 0x1 << 4,
+ NSEventPhaseMayBegin = 0x1 << 5
};
typedef NSUInteger NSEventPhase;
@@ -31,9 +32,19 @@ enum {
};
typedef NSUInteger NSEventSwipeTrackingOptions;
+enum {
+ NSWindowAnimationBehaviorDefault = 0,
+ NSWindowAnimationBehaviorNone = 2,
+ NSWindowAnimationBehaviorDocumentWindow = 3,
+ NSWindowAnimationBehaviorUtilityWindow = 4,
+ NSWindowAnimationBehaviorAlertPanel = 5
+};
+typedef NSInteger NSWindowAnimationBehavior;
+
@interface NSEvent (LionSDK)
+ (BOOL)isSwipeTrackingFromScrollEventsEnabled;
+- (NSEventPhase)momentumPhase;
- (NSEventPhase)phase;
- (CGFloat)scrollingDeltaX;
- (CGFloat)scrollingDeltaY;
@@ -61,6 +72,8 @@ typedef NSUInteger NSEventSwipeTrackingOptions;
@interface NSWindow (LionSDK)
- (CGFloat)backingScaleFactor;
+- (NSWindowAnimationBehavior)animationBehavior;
+- (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
@end
#endif // MAC_OS_X_VERSION_10_7
diff --git a/chromium/base/memory/aligned_memory.cc b/chromium/base/memory/aligned_memory.cc
index b6278aba2cf..5ec88b16b81 100644
--- a/chromium/base/memory/aligned_memory.cc
+++ b/chromium/base/memory/aligned_memory.cc
@@ -6,7 +6,7 @@
#include "base/logging.h"
-#if defined(OS_ANDROID) || defined(OS_NACL)
+#if defined(OS_ANDROID)
#include <malloc.h>
#endif
@@ -19,13 +19,12 @@ void* AlignedAlloc(size_t size, size_t alignment) {
void* ptr = NULL;
#if defined(COMPILER_MSVC)
ptr = _aligned_malloc(size, alignment);
-// Both Android and NaCl technically support posix_memalign(), but do not expose
-// it in the current version of the library headers used by Chrome. Luckily,
-// memalign() on both platforms returns pointers which can safely be used with
-// free(), so we can use it instead. Issues filed with each project for docs:
+// Android technically supports posix_memalign(), but does not expose it in
+// the current version of the library headers used by Chrome. Luckily,
+// memalign() on Android returns pointers which can safely be used with
+// free(), so we can use it instead. Issue filed to document this:
// http://code.google.com/p/android/issues/detail?id=35391
-// http://code.google.com/p/chromium/issues/detail?id=138579
-#elif defined(OS_ANDROID) || defined(OS_NACL)
+#elif defined(OS_ANDROID)
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size))
diff --git a/chromium/base/memory/discardable_memory.cc b/chromium/base/memory/discardable_memory.cc
deleted file mode 100644
index 33f9e19cdee..00000000000
--- a/chromium/base/memory/discardable_memory.cc
+++ /dev/null
@@ -1,66 +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/memory/discardable_memory.h"
-
-#include "base/logging.h"
-
-namespace base {
-
-DiscardableMemory::DiscardableMemory()
- : memory_(NULL),
- size_(0),
- is_locked_(false)
-#if defined(OS_ANDROID)
- , fd_(-1)
-#endif // OS_ANDROID
- {
- DCHECK(Supported());
-}
-
-void* DiscardableMemory::Memory() const {
- DCHECK(is_locked_);
- return memory_;
-}
-
-// Stub implementations for platforms that don't support discardable memory.
-
-#if !defined(OS_ANDROID) && !defined(OS_MACOSX)
-
-DiscardableMemory::~DiscardableMemory() {
- NOTIMPLEMENTED();
-}
-
-// static
-bool DiscardableMemory::Supported() {
- return false;
-}
-
-bool DiscardableMemory::InitializeAndLock(size_t size) {
- NOTIMPLEMENTED();
- return false;
-}
-
-LockDiscardableMemoryStatus DiscardableMemory::Lock() {
- NOTIMPLEMENTED();
- return DISCARDABLE_MEMORY_FAILED;
-}
-
-void DiscardableMemory::Unlock() {
- NOTIMPLEMENTED();
-}
-
-// static
-bool DiscardableMemory::PurgeForTestingSupported() {
- return false;
-}
-
-// static
-void DiscardableMemory::PurgeForTesting() {
- NOTIMPLEMENTED();
-}
-
-#endif // OS_*
-
-} // namespace base
diff --git a/chromium/base/memory/discardable_memory.h b/chromium/base/memory/discardable_memory.h
index d6e91b979d5..cbc2db630a8 100644
--- a/chromium/base/memory/discardable_memory.h
+++ b/chromium/base/memory/discardable_memory.h
@@ -8,6 +8,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
namespace base {
@@ -37,44 +38,42 @@ enum LockDiscardableMemoryStatus {
// - Because of memory alignment, the amount of memory allocated can be
// larger than the requested memory size. It is not very efficient for
// small allocations.
+// - A discardable memory instance is not thread safe. It is the
+// responsibility of users of discardable memory to ensure there are no
+// races.
//
// References:
// - Linux: http://lwn.net/Articles/452035/
// - Mac: http://trac.webkit.org/browser/trunk/Source/WebCore/platform/mac/PurgeableBufferMac.cpp
// the comment starting with "vm_object_purgable_control" at
// http://www.opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/vm/vm_object.c
+//
+// Thread-safety: DiscardableMemory instances are not thread-safe.
class BASE_EXPORT DiscardableMemory {
public:
- DiscardableMemory();
-
- // If the discardable memory is locked, the destructor will unlock it.
- // The opened file will also be closed after this.
- ~DiscardableMemory();
+ virtual ~DiscardableMemory() {}
- // Check whether the system supports discardable memory.
- static bool Supported();
+ // Check whether the system supports discardable memory natively. Returns
+ // false if the support is emulated.
+ static bool SupportedNatively();
- // Initialize the DiscardableMemory object. On success, this function returns
- // true and the memory is locked. This should only be called once.
- // This call could fail because of platform-specific limitations and the user
- // should stop using the DiscardableMemory afterwards.
- bool InitializeAndLock(size_t size);
+ static scoped_ptr<DiscardableMemory> CreateLockedMemory(size_t size);
- // Lock the memory so that it will not be purged by the system. Returns
+ // 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.
- LockDiscardableMemoryStatus Lock() WARN_UNUSED_RESULT;
+ virtual LockDiscardableMemoryStatus Lock() WARN_UNUSED_RESULT = 0;
- // Unlock the memory so that it can be purged by the system. Must be called
+ // Unlocks the memory so that it can be purged by the system. Must be called
// after every successful lock call.
- void Unlock();
+ virtual void Unlock() = 0;
- // Return the memory address held by this object. The object must be locked
+ // Returns the memory address held by this object. The object must be locked
// before calling this. Otherwise, this will cause a DCHECK error.
- void* Memory() const;
+ virtual void* Memory() const = 0;
// Testing utility calls.
@@ -85,32 +84,6 @@ class BASE_EXPORT DiscardableMemory {
// 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();
-
- private:
-#if defined(OS_ANDROID)
- // Maps the discardable memory into the caller's address space.
- // Returns true on success, false otherwise.
- bool Map();
-
- // Unmaps the discardable memory from the caller's address space.
- void Unmap();
-
- // Reserve a file descriptor. When reaching the fd limit, this call returns
- // false and initialization should fail.
- bool ReserveFileDescriptor();
-
- // Release a file descriptor so that others can reserve it.
- void ReleaseFileDescriptor();
-#endif // OS_ANDROID
-
- void* memory_;
- size_t size_;
- bool is_locked_;
-#if defined(OS_ANDROID)
- int fd_;
-#endif // OS_ANDROID
-
- DISALLOW_COPY_AND_ASSIGN(DiscardableMemory);
};
} // namespace base
diff --git a/chromium/base/memory/discardable_memory_allocator_android.cc b/chromium/base/memory/discardable_memory_allocator_android.cc
new file mode 100644
index 00000000000..5e108176fe0
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_allocator_android.cc
@@ -0,0 +1,418 @@
+// 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 <algorithm>
+#include <cmath>
+#include <set>
+#include <utility>
+
+#include "base/basictypes.h"
+#include "base/containers/hash_tables.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"
+
+// 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).
+// - 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.
+
+namespace base {
+namespace {
+
+// Only tolerate fragmentation in used chunks *caused by the client* (as opposed
+// to the allocator when a free chunk is reused). The client can cause such
+// fragmentation by e.g. requesting 4097 bytes. This size would be rounded up to
+// 8192 by the allocator which would cause 4095 bytes of fragmentation (which is
+// currently the maximum allowed). If the client requests 4096 bytes and a free
+// chunk of 8192 bytes is available then the free chunk gets splitted into two
+// pieces to minimize fragmentation (since 8192 - 4096 = 4096 which is greater
+// than 4095).
+// TODO(pliard): tune this if splitting chunks too often leads to performance
+// issues.
+const size_t kMaxChunkFragmentationBytes = 4096 - 1;
+
+} // namespace
+
+namespace internal {
+
+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) {
+ }
+
+ // 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_);
+ }
+
+ virtual void Unlock() OVERRIDE {
+ DCHECK(locked_);
+ locked_ = false;
+ internal::UnlockAshmemRegion(fd_, offset_, size_, address_);
+ }
+
+ virtual void* Memory() const OVERRIDE {
+ return address_;
+ }
+
+ private:
+ 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);
+};
+
+class DiscardableMemoryAllocator::AshmemRegion {
+ public:
+ // Note that |allocator| must outlive |this|.
+ static scoped_ptr<AshmemRegion> Create(
+ size_t size,
+ const std::string& name,
+ DiscardableMemoryAllocator* allocator) {
+ int fd;
+ void* base;
+ if (!internal::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_);
+ DCHECK(result);
+ }
+
+ // Returns a new instance of DiscardableMemory 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
+ // ReuseFreeChunk_Locked() below for more information.
+ // 2) If no free chunk could be reused and the region is not big enough for
+ // the requested size then NULL is returned.
+ // 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) {
+ DCHECK_LE(client_requested_size, actual_size);
+ allocator_->lock_.AssertAcquired();
+ scoped_ptr<DiscardableMemory> 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>();
+ }
+ 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;
+ offset_ += actual_size;
+ DCHECK_LE(offset_, size_);
+ return memory.Pass();
+ }
+
+ void OnChunkDeletion(void* chunk, size_t size) {
+ AutoLock auto_lock(allocator_->lock_);
+ MergeAndAddFreeChunk_Locked(chunk, size);
+ // Note that |this| might be deleted beyond this point.
+ }
+
+ private:
+ struct FreeChunk {
+ FreeChunk(void* previous_chunk, void* start, size_t size)
+ : previous_chunk(previous_chunk),
+ start(start),
+ size(size) {
+ }
+
+ void* const previous_chunk;
+ void* const start;
+ const size_t size;
+
+ bool is_null() const { return !start; }
+
+ bool operator<(const FreeChunk& other) const {
+ return size < other.size;
+ }
+ };
+
+ // Note that |allocator| must outlive |this|.
+ AshmemRegion(int fd,
+ size_t size,
+ void* base,
+ DiscardableMemoryAllocator* allocator)
+ : fd_(fd),
+ size_(size),
+ base_(base),
+ allocator_(allocator),
+ highest_allocated_chunk_(NULL),
+ offset_(0) {
+ DCHECK_GE(fd_, 0);
+ DCHECK_GE(size, kMinAshmemRegionSize);
+ DCHECK(base);
+ DCHECK(allocator);
+ }
+
+ // Tries to reuse a previously freed chunk by doing a closest size match.
+ scoped_ptr<DiscardableMemory> 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)));
+ if (reused_chunk.is_null())
+ return scoped_ptr<DiscardableMemory>();
+
+ used_to_previous_chunk_map_.insert(
+ std::make_pair(reused_chunk.start, reused_chunk.previous_chunk));
+ size_t reused_chunk_size = reused_chunk.size;
+ // |client_requested_size| is used below rather than |actual_size| to
+ // reflect the amount of bytes that would not be usable by the client (i.e.
+ // wasted). Using |actual_size| instead would not allow us to detect
+ // fragmentation caused by the client if he did misaligned allocations.
+ 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
+ // memory pressure.
+ reused_chunk_size = actual_size;
+ void* const new_chunk_start =
+ static_cast<char*>(reused_chunk.start) + actual_size;
+ 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
+ // free chunks at this point.
+ 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));
+ return memory.Pass();
+ }
+
+ // Makes the chunk identified with the provided arguments free and possibly
+ // merges this chunk with the previous and next contiguous ones.
+ // If the provided chunk is the only one used (and going to be freed) in the
+ // region then the internal ashmem region is closed so that the underlying
+ // physical pages are immediately released.
+ // Note that free chunks are unlocked therefore they can be reclaimed by the
+ // kernel if needed (under memory pressure) but they are not immediately
+ // released unfortunately since madvise(MADV_REMOVE) and
+ // fallocate(FALLOC_FL_PUNCH_HOLE) don't seem to work on ashmem. This might
+ // change in versions of kernel >=3.5 though. The fact that free chunks are
+ // not immediately released is the reason why we are trying to minimize
+ // fragmentation in order not to cause "artificial" memory pressure.
+ void MergeAndAddFreeChunk_Locked(void* chunk, size_t size) {
+ allocator_->lock_.AssertAcquired();
+ size_t new_free_chunk_size = size;
+ // Merge with the previous chunk.
+ void* first_free_chunk = chunk;
+ DCHECK(!used_to_previous_chunk_map_.empty());
+ const hash_map<void*, void*>::iterator previous_chunk_it =
+ used_to_previous_chunk_map_.find(chunk);
+ 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;
+ // There should not be more contiguous previous free chunks.
+ DCHECK(!address_to_free_chunk_map_.count(free_chunk.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;
+ // 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) {
+ AddFreeChunk_Locked(
+ 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(free_chunks_.empty());
+ DCHECK(address_to_free_chunk_map_.empty());
+ DCHECK(used_to_previous_chunk_map_.empty());
+ allocator_->DeleteAshmemRegion_Locked(this); // Deletes |this|.
+ }
+
+ void AddFreeChunk_Locked(const FreeChunk& free_chunk) {
+ allocator_->lock_.AssertAcquired();
+ const std::multiset<FreeChunk>::iterator it = free_chunks_.insert(
+ free_chunk);
+ address_to_free_chunk_map_.insert(std::make_pair(free_chunk.start, it));
+ // Update the next used contiguous chunk, if any, since its previous chunk
+ // may have changed due to free chunks merging/splitting.
+ void* const next_used_contiguous_chunk =
+ static_cast<char*>(free_chunk.start) + free_chunk.size;
+ hash_map<void*, void*>::iterator previous_it =
+ used_to_previous_chunk_map_.find(next_used_contiguous_chunk);
+ if (previous_it != used_to_previous_chunk_map_.end())
+ previous_it->second = free_chunk.start;
+ }
+
+ // Finds and removes the free chunk, if any, whose start address is
+ // |chunk_start|. Returns a copy of the unlinked free chunk or a free chunk
+ // whose content is null if it was not found.
+ FreeChunk RemoveFreeChunk_Locked(void* chunk_start) {
+ allocator_->lock_.AssertAcquired();
+ const hash_map<
+ 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 RemoveFreeChunkFromIterator_Locked(it->second);
+ }
+
+ // Same as above but takes an iterator in.
+ FreeChunk RemoveFreeChunkFromIterator_Locked(
+ std::multiset<FreeChunk>::iterator free_chunk_it) {
+ allocator_->lock_.AssertAcquired();
+ if (free_chunk_it == free_chunks_.end())
+ return FreeChunk(NULL, NULL, 0U);
+ DCHECK(free_chunk_it != free_chunks_.end());
+ const FreeChunk free_chunk(*free_chunk_it);
+ address_to_free_chunk_map_.erase(free_chunk_it->start);
+ free_chunks_.erase(free_chunk_it);
+ return free_chunk;
+ }
+
+ const int fd_;
+ const size_t size_;
+ void* const base_;
+ DiscardableMemoryAllocator* const allocator_;
+ void* highest_allocated_chunk_;
+ // Points to the end of |highest_allocated_chunk_|.
+ size_t offset_;
+ // Allows free chunks recycling (lookup, insertion and removal) in O(log N).
+ // Note that FreeChunk values are indexed by their size and also note that
+ // multiple free chunks can have the same size (which is why multiset<> is
+ // used instead of e.g. set<>).
+ std::multiset<FreeChunk> free_chunks_;
+ // Used while merging free contiguous chunks to erase free chunks (from their
+ // start address) in constant time. Note that multiset<>::{insert,erase}()
+ // don't invalidate iterators (except the one for the element being removed
+ // obviously).
+ hash_map<
+ void*, std::multiset<FreeChunk>::iterator> address_to_free_chunk_map_;
+ // Maps the address of *used* chunks to the address of their previous
+ // contiguous chunk.
+ hash_map<void*, void*> used_to_previous_chunk_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(AshmemRegion);
+};
+
+DiscardableMemoryAllocator::DiscardableAshmemChunk::~DiscardableAshmemChunk() {
+ if (locked_)
+ internal::UnlockAshmemRegion(fd_, offset_, size_, address_);
+ ashmem_region_->OnChunkDeletion(address_, size_);
+}
+
+DiscardableMemoryAllocator::DiscardableMemoryAllocator(const std::string& name)
+ : name_(name) {
+}
+
+DiscardableMemoryAllocator::~DiscardableMemoryAllocator() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(ashmem_regions_.empty());
+}
+
+scoped_ptr<DiscardableMemory> DiscardableMemoryAllocator::Allocate(
+ size_t size) {
+ const size_t aligned_size = internal::AlignToNextPage(size);
+ // 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
+ // couple (=5) of AshmemRegion instances in practice though.
+ AutoLock auto_lock(lock_);
+ DCHECK_LE(ashmem_regions_.size(), 5U);
+ for (ScopedVector<AshmemRegion>::iterator it = ashmem_regions_.begin();
+ it != ashmem_regions_.end(); ++it) {
+ scoped_ptr<DiscardableMemory> 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>();
+ }
+ ashmem_regions_.push_back(new_region.release());
+ return ashmem_regions_.back()->Allocate_Locked(size, aligned_size);
+}
+
+void DiscardableMemoryAllocator::DeleteAshmemRegion_Locked(
+ AshmemRegion* region) {
+ lock_.AssertAcquired();
+ // Note that there should not be more than a couple of ashmem region instances
+ // in |ashmem_regions_|.
+ DCHECK_LE(ashmem_regions_.size(), 5U);
+ const ScopedVector<AshmemRegion>::iterator it = std::find(
+ ashmem_regions_.begin(), ashmem_regions_.end(), region);
+ DCHECK_NE(ashmem_regions_.end(), it);
+ std::swap(*it, ashmem_regions_.back());
+ ashmem_regions_.pop_back();
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_allocator_android.h b/chromium/base/memory/discardable_memory_allocator_android.h
new file mode 100644
index 00000000000..7991656cff6
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_allocator_android.h
@@ -0,0 +1,65 @@
+// 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
new file mode 100644
index 00000000000..97cf5d45f72
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_allocator_android_unittest.cc
@@ -0,0 +1,232 @@
+// 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 73a25ae419e..7e84967055f 100644
--- a/chromium/base/memory/discardable_memory_android.cc
+++ b/chromium/base/memory/discardable_memory_android.cc
@@ -2,153 +2,248 @@
// 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/memory/discardable_memory_android.h"
#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
#include <unistd.h>
+#include <limits>
+
+#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/posix/eintr_wrapper.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"
+namespace base {
namespace {
-base::LazyInstance<base::Lock>::Leaky g_discardable_memory_lock =
- LAZY_INSTANCE_INITIALIZER;
+const size_t kPageSize = 4096;
-// Total number of discardable memory in the process.
-int g_num_discardable_memory = 0;
+const char kAshmemAllocatorName[] = "DiscardableMemoryAllocator";
-// Upper limit on the number of discardable memory to avoid hitting file
-// descriptor limit.
-const int kDiscardableMemoryNumLimit = 128;
+struct GlobalContext {
+ GlobalContext()
+ : ashmem_fd_limit(GetSoftFDLimit()),
+ allocator(kAshmemAllocatorName),
+ ashmem_fd_count_(0) {
+ }
-}
+ const int ashmem_fd_limit;
+ internal::DiscardableMemoryAllocator allocator;
+ Lock lock;
-namespace base {
+ int ashmem_fd_count() const {
+ lock.AssertAcquired();
+ return ashmem_fd_count_;
+ }
-// static
-bool DiscardableMemory::Supported() {
- return true;
-}
+ void decrement_ashmem_fd_count() {
+ lock.AssertAcquired();
+ --ashmem_fd_count_;
+ }
-DiscardableMemory::~DiscardableMemory() {
- if (is_locked_)
- Unlock();
- // If fd_ is smaller than 0, initialization must have failed and
- // g_num_discardable_memory is not incremented by the caller.
- if (fd_ < 0)
- return;
- HANDLE_EINTR(close(fd_));
- fd_ = -1;
- ReleaseFileDescriptor();
-}
+ void increment_ashmem_fd_count() {
+ lock.AssertAcquired();
+ ++ashmem_fd_count_;
+ }
-bool DiscardableMemory::ReserveFileDescriptor() {
- base::AutoLock lock(g_discardable_memory_lock.Get());
- if (g_num_discardable_memory < kDiscardableMemoryNumLimit) {
- ++g_num_discardable_memory;
- return true;
+ 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;
}
- return false;
+
+ 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_);
+ }
+
+ virtual void Unlock() OVERRIDE {
+ internal::UnlockAshmemRegion(fd_, 0, size_, memory_);
+ }
+
+ virtual void* Memory() const OVERRIDE {
+ return memory_;
+ }
+
+ 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();
}
-void DiscardableMemory::ReleaseFileDescriptor() {
- base::AutoLock lock(g_discardable_memory_lock.Get());
- --g_num_discardable_memory;
- DCHECK_LE(0, g_num_discardable_memory);
+// 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;
}
-bool DiscardableMemory::InitializeAndLock(size_t size) {
- // When this function returns true, fd_ should be larger or equal than 0
- // and g_num_discardable_memory is incremented by 1. Otherwise, fd_
- // is less than 0 and g_num_discardable_memory is not incremented by
- // the caller.
- DCHECK_EQ(fd_, -1);
- DCHECK(!memory_);
- if (!ReserveFileDescriptor())
- return false;
+} // namespace
+
+namespace internal {
- size_ = size;
- fd_ = ashmem_create_region("", size);
+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;
+}
- if (fd_ < 0) {
+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";
- ReleaseFileDescriptor();
return false;
}
+ file_util::ScopedFD fd_closer(&fd);
- int err = ashmem_set_prot_region(fd_, PROT_READ | PROT_WRITE);
+ const int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
if (err < 0) {
DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
- HANDLE_EINTR(close(fd_));
- fd_ = -1;
- ReleaseFileDescriptor();
return false;
}
- if (!Map()) {
- // Close the file descriptor in case of any initialization errors.
- HANDLE_EINTR(close(fd_));
- fd_ = -1;
- ReleaseFileDescriptor();
+ // 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;
}
- is_locked_ = true;
+ ignore_result(fd_closer.release());
+ g_context.Get().increment_ashmem_fd_count();
+ *out_fd = fd;
+ *out_address = address;
return true;
}
-LockDiscardableMemoryStatus DiscardableMemory::Lock() {
- DCHECK_NE(fd_, -1);
- DCHECK(!is_locked_);
-
- bool purged = false;
- if (ashmem_pin_region(fd_, 0, 0) == ASHMEM_WAS_PURGED)
- purged = true;
-
- if (!Map())
- return DISCARDABLE_MEMORY_FAILED;
-
- is_locked_ = true;
- return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
+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;
}
-void DiscardableMemory::Unlock() {
- DCHECK_GE(fd_, 0);
- DCHECK(is_locked_);
+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;
+}
- Unmap();
- if (ashmem_unpin_region(fd_, 0, 0))
+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.";
- is_locked_ = false;
+ // This allows us to catch accesses to unlocked memory.
+ DCHECK_EQ(0, mprotect(address, size, PROT_NONE));
+ return !failed;
}
-bool DiscardableMemory::Map() {
- DCHECK(!memory_);
- // 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.
- memory_ = mmap(NULL, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0);
- if (memory_ == (void*)-1) {
- DPLOG(ERROR) << "Failed to map memory.";
- memory_ = NULL;
- if (ashmem_unpin_region(fd_, 0, 0))
- DLOG(ERROR) << "Failed to unpin memory.";
- return false;
- }
+} // namespace internal
+
+// static
+bool DiscardableMemory::SupportedNatively() {
return true;
}
-void DiscardableMemory::Unmap() {
- DCHECK(memory_);
-
- if (-1 == munmap(memory_, size_))
- DPLOG(ERROR) << "Failed to unmap memory.";
-
- memory_ = NULL;
+// 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));
+ }
+ }
+ return global_context->allocator.Allocate(size);
}
// static
diff --git a/chromium/base/memory/discardable_memory_android.h b/chromium/base/memory/discardable_memory_android.h
new file mode 100644
index 00000000000..9db78b33853
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_android.h
@@ -0,0 +1,37 @@
+// 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_emulated.cc b/chromium/base/memory/discardable_memory_emulated.cc
new file mode 100644
index 00000000000..ed7c42c750f
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_emulated.cc
@@ -0,0 +1,65 @@
+// 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_emulated.h"
+
+#include "base/lazy_instance.h"
+#include "base/memory/discardable_memory_provider.h"
+
+namespace base {
+
+namespace {
+
+base::LazyInstance<internal::DiscardableMemoryProvider>::Leaky g_provider =
+ LAZY_INSTANCE_INITIALIZER;
+
+} // namespace
+
+namespace internal {
+
+DiscardableMemoryEmulated::DiscardableMemoryEmulated(size_t size)
+ : is_locked_(false) {
+ g_provider.Pointer()->Register(this, size);
+}
+
+DiscardableMemoryEmulated::~DiscardableMemoryEmulated() {
+ if (is_locked_)
+ Unlock();
+ g_provider.Pointer()->Unregister(this);
+}
+
+bool DiscardableMemoryEmulated::Initialize() {
+ return Lock() == DISCARDABLE_MEMORY_PURGED;
+}
+
+LockDiscardableMemoryStatus DiscardableMemoryEmulated::Lock() {
+ DCHECK(!is_locked_);
+
+ bool purged = false;
+ memory_ = g_provider.Pointer()->Acquire(this, &purged);
+ if (!memory_)
+ return DISCARDABLE_MEMORY_FAILED;
+
+ is_locked_ = true;
+ return purged ? DISCARDABLE_MEMORY_PURGED : DISCARDABLE_MEMORY_SUCCESS;
+}
+
+void DiscardableMemoryEmulated::Unlock() {
+ DCHECK(is_locked_);
+ g_provider.Pointer()->Release(this, memory_.Pass());
+ is_locked_ = false;
+}
+
+void* DiscardableMemoryEmulated::Memory() const {
+ DCHECK(memory_);
+ return memory_.get();
+}
+
+// static
+void DiscardableMemoryEmulated::PurgeForTesting() {
+ g_provider.Pointer()->PurgeAll();
+}
+
+} // namespace internal
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_emulated.h b/chromium/base/memory/discardable_memory_emulated.h
new file mode 100644
index 00000000000..bd0e834ed06
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_emulated.h
@@ -0,0 +1,37 @@
+// 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_EMULATED_H_
+#define BASE_MEMORY_DISCARDABLE_MEMORY_EMULATED_H_
+
+#include "base/memory/discardable_memory.h"
+
+namespace base {
+namespace internal {
+
+class DiscardableMemoryEmulated : public DiscardableMemory {
+ public:
+ explicit DiscardableMemoryEmulated(size_t size);
+ virtual ~DiscardableMemoryEmulated();
+
+ static void PurgeForTesting();
+
+ bool Initialize();
+
+ // Overridden from DiscardableMemory:
+ virtual LockDiscardableMemoryStatus Lock() OVERRIDE;
+ virtual void Unlock() OVERRIDE;
+ virtual void* Memory() const OVERRIDE;
+
+ private:
+ scoped_ptr<uint8, FreeDeleter> memory_;
+ bool is_locked_;
+
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryEmulated);
+};
+
+} // namespace internal
+} // namespace base
+
+#endif // BASE_MEMORY_DISCARDABLE_MEMORY_EMULATED_H_
diff --git a/chromium/base/memory/discardable_memory_linux.cc b/chromium/base/memory/discardable_memory_linux.cc
new file mode 100644
index 00000000000..92e39e5e7d4
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_linux.cc
@@ -0,0 +1,35 @@
+// 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_emulated.h"
+
+namespace base {
+
+// static
+bool DiscardableMemory::SupportedNatively() {
+ return false;
+}
+
+// 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>();
+}
+
+// static
+bool DiscardableMemory::PurgeForTestingSupported() {
+ return true;
+}
+
+// static
+void DiscardableMemory::PurgeForTesting() {
+ internal::DiscardableMemoryEmulated::PurgeForTesting();
+}
+
+} // namespace base
diff --git a/chromium/base/memory/discardable_memory_mac.cc b/chromium/base/memory/discardable_memory_mac.cc
index 1dbb6ad1fd2..aa6823509d5 100644
--- a/chromium/base/memory/discardable_memory_mac.cc
+++ b/chromium/base/memory/discardable_memory_mac.cc
@@ -5,11 +5,14 @@
#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/logging.h"
+#include "base/memory/scoped_ptr.h"
namespace base {
-
namespace {
// The VM subsystem allows tagging of memory and 240-255 is reserved for
@@ -17,25 +20,68 @@ namespace {
// weight of ~52).
const int kDiscardableMemoryTag = VM_MAKE_TAG(252);
-} // namespace
-
-// static
-bool DiscardableMemory::Supported() {
- return true;
-}
+class DiscardableMemoryMac : public DiscardableMemory {
+ public:
+ DiscardableMemoryMac(void* memory, size_t size)
+ : memory_(memory),
+ size_(size) {
+ DCHECK(memory_);
+ }
-DiscardableMemory::~DiscardableMemory() {
- if (memory_) {
+ virtual ~DiscardableMemoryMac() {
vm_deallocate(mach_task_self(),
reinterpret_cast<vm_address_t>(memory_),
size_);
}
-}
-bool DiscardableMemory::InitializeAndLock(size_t size) {
- DCHECK(!memory_);
- size_ = size;
+ 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;
+ }
+
+ 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.";
+ }
+
+ virtual void* Memory() const OVERRIDE {
+ return memory_;
+ }
+
+ private:
+ void* const memory_;
+ const size_t size_;
+ DISALLOW_COPY_AND_ASSIGN(DiscardableMemoryMac);
+};
+
+} // namespace
+
+// static
+bool DiscardableMemory::SupportedNatively() {
+ return true;
+}
+
+// static
+scoped_ptr<DiscardableMemory> DiscardableMemory::CreateLockedMemory(
+ size_t size) {
vm_address_t buffer = 0;
kern_return_t ret = vm_allocate(mach_task_self(),
&buffer,
@@ -43,49 +89,12 @@ bool DiscardableMemory::InitializeAndLock(size_t size) {
VM_FLAGS_PURGABLE |
VM_FLAGS_ANYWHERE |
kDiscardableMemoryTag);
-
if (ret != KERN_SUCCESS) {
DLOG(ERROR) << "vm_allocate() failed";
- return false;
+ return scoped_ptr<DiscardableMemory>();
}
-
- is_locked_ = true;
- memory_ = reinterpret_cast<void*>(buffer);
- return true;
-}
-
-LockDiscardableMemoryStatus DiscardableMemory::Lock() {
- DCHECK(!is_locked_);
-
- 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;
-
- is_locked_ = true;
- return state & VM_PURGABLE_EMPTY ? DISCARDABLE_MEMORY_PURGED
- : DISCARDABLE_MEMORY_SUCCESS;
-}
-
-void DiscardableMemory::Unlock() {
- DCHECK(is_locked_);
-
- 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);
-
- if (ret != KERN_SUCCESS)
- DLOG(ERROR) << "Failed to unlock memory.";
-
- is_locked_ = false;
+ return scoped_ptr<DiscardableMemory>(
+ new DiscardableMemoryMac(reinterpret_cast<void*>(buffer), size));
}
// static
diff --git a/chromium/base/memory/discardable_memory_provider.cc b/chromium/base/memory/discardable_memory_provider.cc
new file mode 100644
index 00000000000..1c84339eba7
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_provider.cc
@@ -0,0 +1,215 @@
+// 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
new file mode 100644
index 00000000000..6c343c0d929
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_provider.h
@@ -0,0 +1,143 @@
+// 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
new file mode 100644
index 00000000000..1559654c789
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_provider_unittest.cc
@@ -0,0 +1,406 @@
+// 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 60d35820fbd..c9f67b2556f 100644
--- a/chromium/base/memory/discardable_memory_unittest.cc
+++ b/chromium/base/memory/discardable_memory_unittest.cc
@@ -3,59 +3,91 @@
// found in the LICENSE file.
#include "base/memory/discardable_memory.h"
+
+#include <limits>
+
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
-#if defined(OS_ANDROID) || defined(OS_MACOSX)
+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);
+}
+#endif
+
+TEST(DiscardableMemoryTest, SupportedNatively) {
+#if defined(DISCARDABLE_MEMORY_ALWAYS_SUPPORTED_NATIVELY)
+ ASSERT_TRUE(DiscardableMemory::SupportedNatively());
+#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());
+#endif
+}
+
// Test Lock() and Unlock() functionalities.
TEST(DiscardableMemoryTest, LockAndUnLock) {
- ASSERT_TRUE(DiscardableMemory::Supported());
-
- const size_t size = 1024;
-
- DiscardableMemory memory;
- ASSERT_TRUE(memory.InitializeAndLock(size));
- void* addr = memory.Memory();
+ const scoped_ptr<DiscardableMemory> memory(
+ DiscardableMemory::CreateLockedMemory(kSize));
+ ASSERT_TRUE(memory);
+ void* addr = memory->Memory();
ASSERT_NE(static_cast<void*>(NULL), addr);
- memory.Unlock();
+ 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());
- addr = memory.Memory();
+ EXPECT_EQ(DISCARDABLE_MEMORY_SUCCESS, memory->Lock());
+ addr = memory->Memory();
ASSERT_NE(static_cast<void*>(NULL), addr);
- memory.Unlock();
+ memory->Unlock();
}
// Test delete a discardable memory while it is locked.
TEST(DiscardableMemoryTest, DeleteWhileLocked) {
- ASSERT_TRUE(DiscardableMemory::Supported());
-
- const size_t size = 1024;
-
- DiscardableMemory memory;
- ASSERT_TRUE(memory.InitializeAndLock(size));
+ const scoped_ptr<DiscardableMemory> memory(
+ DiscardableMemory::CreateLockedMemory(kSize));
+ ASSERT_TRUE(memory);
}
-#if defined(OS_MACOSX)
+#if !defined(OS_ANDROID)
// Test forced purging.
TEST(DiscardableMemoryTest, Purge) {
- ASSERT_TRUE(DiscardableMemory::Supported());
ASSERT_TRUE(DiscardableMemory::PurgeForTestingSupported());
- const size_t size = 1024;
-
- DiscardableMemory memory;
- ASSERT_TRUE(memory.InitializeAndLock(size));
- memory.Unlock();
+ const scoped_ptr<DiscardableMemory> memory(
+ DiscardableMemory::CreateLockedMemory(kSize));
+ ASSERT_TRUE(memory);
+ memory->Unlock();
DiscardableMemory::PurgeForTesting();
- EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, memory.Lock());
+ EXPECT_EQ(DISCARDABLE_MEMORY_PURGED, memory->Lock());
}
-#endif // OS_MACOSX
-
-#endif // OS_*
+#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));
+ ASSERT_TRUE(memory);
+ memory->Unlock();
+ ASSERT_DEATH_IF_SUPPORTED(
+ { *static_cast<int*>(memory->Memory()) = 0xdeadbeef; }, ".*");
+}
+#endif
}
diff --git a/chromium/base/memory/discardable_memory_win.cc b/chromium/base/memory/discardable_memory_win.cc
new file mode 100644
index 00000000000..92e39e5e7d4
--- /dev/null
+++ b/chromium/base/memory/discardable_memory_win.cc
@@ -0,0 +1,35 @@
+// 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_emulated.h"
+
+namespace base {
+
+// static
+bool DiscardableMemory::SupportedNatively() {
+ return false;
+}
+
+// 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>();
+}
+
+// static
+bool DiscardableMemory::PurgeForTestingSupported() {
+ return true;
+}
+
+// static
+void DiscardableMemory::PurgeForTesting() {
+ internal::DiscardableMemoryEmulated::PurgeForTesting();
+}
+
+} // namespace base
diff --git a/chromium/base/memory/memory_pressure_listener.h b/chromium/base/memory/memory_pressure_listener.h
index 7b8029dd712..90eb144f4b4 100644
--- a/chromium/base/memory/memory_pressure_listener.h
+++ b/chromium/base/memory/memory_pressure_listener.h
@@ -24,11 +24,8 @@ namespace base {
// simply delete the listener object. The implementation guarantees
// that the callback will always be called on the thread that created
// the listener.
-// If this is the same thread as the system is broadcasting the memory pressure
-// event on, then it is guaranteed you're called synchronously within that
-// broadcast and hence you should not do long-running garbage collection work.
-// But conversely, if there's something that needs to be released before
-// control is returned to system code, this is the place to do it.
+// Note that even on the same thread, the callback is not guaranteed to be
+// called synchronously within the system memory pressure broadcast.
// Please see notes on memory_pressure_level_list.h: some levels are absolutely
// critical, and if not enough memory is returned to the system, it'll
// potentially kill the app, and then later the app will have to be
diff --git a/chromium/base/memory/ref_counted.h b/chromium/base/memory/ref_counted.h
index 0c6a71fdb3e..aae36b17c2f 100644
--- a/chromium/base/memory/ref_counted.h
+++ b/chromium/base/memory/ref_counted.h
@@ -251,7 +251,11 @@ class scoped_refptr {
}
T* get() const { return ptr_; }
+
+ // Allow scoped_refptr<C> to be used in boolean expression
+ // and comparison operations.
operator T*() const { return ptr_; }
+
T* operator->() const {
assert(ptr_ != NULL);
return ptr_;
diff --git a/chromium/base/memory/ref_counted_memory.cc b/chromium/base/memory/ref_counted_memory.cc
index b048a6e0d8d..b1deee11201 100644
--- a/chromium/base/memory/ref_counted_memory.cc
+++ b/chromium/base/memory/ref_counted_memory.cc
@@ -4,6 +4,8 @@
#include "base/memory/ref_counted_memory.h"
+#include <stdlib.h>
+
#include "base/logging.h"
namespace base {
@@ -74,4 +76,22 @@ size_t RefCountedString::size() const {
return data_.size();
}
+RefCountedMallocedMemory::RefCountedMallocedMemory(
+ void* data, size_t length)
+ : data_(reinterpret_cast<unsigned char*>(data)), length_(length) {
+ DCHECK(data || length == 0);
+}
+
+const unsigned char* RefCountedMallocedMemory::front() const {
+ return length_ ? data_ : NULL;
+}
+
+size_t RefCountedMallocedMemory::size() const {
+ return length_;
+}
+
+RefCountedMallocedMemory::~RefCountedMallocedMemory() {
+ free(data_);
+}
+
} // namespace base
diff --git a/chromium/base/memory/ref_counted_memory.h b/chromium/base/memory/ref_counted_memory.h
index fd5e8a0b8fc..d2987c5d21b 100644
--- a/chromium/base/memory/ref_counted_memory.h
+++ b/chromium/base/memory/ref_counted_memory.h
@@ -113,6 +113,26 @@ class BASE_EXPORT RefCountedString : public RefCountedMemory {
DISALLOW_COPY_AND_ASSIGN(RefCountedString);
};
+// An implementation of RefCountedMemory that holds a chunk of memory
+// previously allocated with malloc or calloc, and that therefore must be freed
+// using free().
+class BASE_EXPORT RefCountedMallocedMemory : public base::RefCountedMemory {
+ public:
+ RefCountedMallocedMemory(void* data, size_t length);
+
+ // Overridden from RefCountedMemory:
+ virtual const unsigned char* front() const OVERRIDE;
+ virtual size_t size() const OVERRIDE;
+
+ private:
+ virtual ~RefCountedMallocedMemory();
+
+ unsigned char* data_;
+ size_t length_;
+
+ DISALLOW_COPY_AND_ASSIGN(RefCountedMallocedMemory);
+};
+
} // namespace base
#endif // BASE_MEMORY_REF_COUNTED_MEMORY_H_
diff --git a/chromium/base/memory/ref_counted_memory_unittest.cc b/chromium/base/memory/ref_counted_memory_unittest.cc
index c6f2b9c3d1c..88c5b534851 100644
--- a/chromium/base/memory/ref_counted_memory_unittest.cc
+++ b/chromium/base/memory/ref_counted_memory_unittest.cc
@@ -42,6 +42,16 @@ TEST(RefCountedMemoryUnitTest, RefCountedString) {
EXPECT_EQ('e', mem->front()[1]);
}
+TEST(RefCountedMemoryUnitTest, RefCountedMallocedMemory) {
+ void* data = malloc(6);
+ memcpy(data, "hello", 6);
+
+ scoped_refptr<RefCountedMemory> mem = new RefCountedMallocedMemory(data, 6);
+
+ EXPECT_EQ(6U, mem->size());
+ EXPECT_EQ(0, memcmp("hello", mem->front(), 6));
+}
+
TEST(RefCountedMemoryUnitTest, Equals) {
std::string s1("same");
scoped_refptr<RefCountedMemory> mem1 = RefCountedString::TakeString(&s1);
diff --git a/chromium/base/memory/ref_counted_unittest.cc b/chromium/base/memory/ref_counted_unittest.cc
index e8eb0fd90f6..7a033f3a2a7 100644
--- a/chromium/base/memory/ref_counted_unittest.cc
+++ b/chromium/base/memory/ref_counted_unittest.cc
@@ -60,3 +60,25 @@ TEST(RefCountedUnitTest, ScopedRefPtrToSelf) {
check->SelfDestruct();
EXPECT_TRUE(ScopedRefPtrToSelf::was_destroyed());
}
+
+TEST(RefCountedUnitTest, ScopedRefPtrBooleanOperations) {
+ scoped_refptr<SelfAssign> p1 = new SelfAssign;
+ scoped_refptr<SelfAssign> p2;
+
+ EXPECT_TRUE(p1);
+ EXPECT_FALSE(!p1);
+
+ EXPECT_TRUE(!p2);
+ EXPECT_FALSE(p2);
+
+ EXPECT_NE(p1, p2);
+
+ SelfAssign* raw_p = new SelfAssign;
+ p2 = raw_p;
+ EXPECT_NE(p1, p2);
+ EXPECT_EQ(raw_p, p2);
+
+ p2 = p1;
+ EXPECT_NE(raw_p, p2);
+ EXPECT_EQ(p1, p2);
+}
diff --git a/chromium/base/memory/scoped_ptr.h b/chromium/base/memory/scoped_ptr.h
index c1fed9ae459..790fecce71e 100644
--- a/chromium/base/memory/scoped_ptr.h
+++ b/chromium/base/memory/scoped_ptr.h
@@ -68,11 +68,11 @@
// is different though because we are constructing a temporary on the return
// line and thus can avoid needing to call Pass().
//
-// Pass() properly handles upcast in assignment, i.e. you can assign
-// scoped_ptr<Child> to scoped_ptr<Parent>:
+// Pass() properly handles upcast in initialization, i.e. you can use a
+// scoped_ptr<Child> to initialize a scoped_ptr<Parent>:
//
// scoped_ptr<Foo> foo(new Foo());
-// scoped_ptr<FooParent> parent = foo.Pass();
+// scoped_ptr<FooParent> parent(foo.Pass());
//
// PassAs<>() should be used to upcast return value in return statement:
//
diff --git a/chromium/base/memory/scoped_vector.h b/chromium/base/memory/scoped_vector.h
index 59144c0e82a..1b30f635ca1 100644
--- a/chromium/base/memory/scoped_vector.h
+++ b/chromium/base/memory/scoped_vector.h
@@ -8,6 +8,7 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/logging.h"
#include "base/move.h"
#include "base/stl_util.h"
@@ -64,6 +65,12 @@ class ScopedVector {
void push_back(T* elem) { v_.push_back(elem); }
+ void pop_back() {
+ DCHECK(!empty());
+ delete v_.back();
+ v_.pop_back();
+ }
+
std::vector<T*>& get() { return v_; }
const std::vector<T*>& get() const { return v_; }
void swap(std::vector<T*>& other) { v_.swap(other); }
diff --git a/chromium/base/memory/scoped_vector_unittest.cc b/chromium/base/memory/scoped_vector_unittest.cc
index 353b52c4e73..efcc04757cc 100644
--- a/chromium/base/memory/scoped_vector_unittest.cc
+++ b/chromium/base/memory/scoped_vector_unittest.cc
@@ -112,6 +112,18 @@ TEST(ScopedVectorTest, LifeCycleWatcher) {
EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
}
+TEST(ScopedVectorTest, PopBack) {
+ LifeCycleWatcher watcher;
+ EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
+ ScopedVector<LifeCycleObject> scoped_vector;
+ scoped_vector.push_back(watcher.NewLifeCycleObject());
+ EXPECT_EQ(LC_CONSTRUCTED, watcher.life_cycle_state());
+ EXPECT_TRUE(watcher.IsWatching(scoped_vector.back()));
+ scoped_vector.pop_back();
+ EXPECT_EQ(LC_DESTROYED, watcher.life_cycle_state());
+ EXPECT_TRUE(scoped_vector.empty());
+}
+
TEST(ScopedVectorTest, Clear) {
LifeCycleWatcher watcher;
EXPECT_EQ(LC_INITIAL, watcher.life_cycle_state());
diff --git a/chromium/base/memory/shared_memory.h b/chromium/base/memory/shared_memory.h
index 23f6973374a..9007aede109 100644
--- a/chromium/base/memory/shared_memory.h
+++ b/chromium/base/memory/shared_memory.h
@@ -21,6 +21,7 @@
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
+#include "base/file_util.h"
#endif
namespace base {
@@ -80,6 +81,11 @@ class BASE_EXPORT SharedMemory {
// Create a new SharedMemory object from an existing, open
// shared memory file.
+ //
+ // WARNING: This does not reduce the OS-level permissions on the handle; it
+ // only affects how the SharedMemory will be mmapped. Use
+ // ShareReadOnlyToProcess to drop permissions. TODO(jln,jyasskin): DCHECK
+ // that |read_only| matches the permissions of the handle.
SharedMemory(SharedMemoryHandle handle, bool read_only);
// Create a new SharedMemory object from an existing, open
@@ -189,15 +195,41 @@ class BASE_EXPORT SharedMemory {
// It is safe to call Close repeatedly.
void Close();
+ // Shares the shared memory to another process. Attempts to create a
+ // platform-specific new_handle which can be used in a remote process to read
+ // the shared memory file. new_handle is an output parameter to receive the
+ // 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.
+ //
+ // Returns true on success, false otherwise.
+ bool ShareReadOnlyToProcess(ProcessHandle process,
+ SharedMemoryHandle* new_handle) {
+ return ShareToProcessCommon(process, new_handle, false, SHARE_READONLY);
+ }
+
+ // Logically equivalent to:
+ // bool ok = ShareReadOnlyToProcess(process, new_handle);
+ // Close();
+ // return ok;
+ // Note that the memory is unmapped by calling this method, regardless of the
+ // return value.
+ bool GiveReadOnlyToProcess(ProcessHandle process,
+ SharedMemoryHandle* new_handle) {
+ return ShareToProcessCommon(process, new_handle, true, SHARE_READONLY);
+ }
+
// Shares the shared memory to another process. Attempts
// to create a platform-specific new_handle which can be
// used in a remote process to access the shared memory
- // file. new_handle is an ouput parameter to receive
+ // file. new_handle is an output parameter to receive
// the handle for use in the remote process.
// Returns true on success, false otherwise.
bool ShareToProcess(ProcessHandle process,
SharedMemoryHandle* new_handle) {
- return ShareToProcessCommon(process, new_handle, false);
+ return ShareToProcessCommon(process, new_handle, false, SHARE_CURRENT_MODE);
}
// Logically equivalent to:
@@ -208,7 +240,7 @@ class BASE_EXPORT SharedMemory {
// return value.
bool GiveToProcess(ProcessHandle process,
SharedMemoryHandle* new_handle) {
- return ShareToProcessCommon(process, new_handle, true);
+ return ShareToProcessCommon(process, new_handle, true, SHARE_CURRENT_MODE);
}
// Locks the shared memory.
@@ -232,19 +264,25 @@ class BASE_EXPORT SharedMemory {
private:
#if defined(OS_POSIX) && !defined(OS_NACL)
- bool PrepareMapFile(FILE *fp);
+ bool PrepareMapFile(file_util::ScopedFILE fp, file_util::ScopedFD readonly);
bool FilePathForMemoryName(const std::string& mem_name, FilePath* path);
void LockOrUnlockCommon(int function);
#endif
+ enum ShareMode {
+ SHARE_READONLY,
+ SHARE_CURRENT_MODE,
+ };
bool ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle* new_handle,
- bool close_self);
+ bool close_self,
+ ShareMode);
#if defined(OS_WIN)
std::wstring name_;
HANDLE mapped_file_;
#elif defined(OS_POSIX)
int mapped_file_;
+ int readonly_mapped_file_;
ino_t inode_;
#endif
size_t mapped_size_;
diff --git a/chromium/base/memory/shared_memory_android.cc b/chromium/base/memory/shared_memory_android.cc
index 3ca3c8fa360..25a65730cb7 100644
--- a/chromium/base/memory/shared_memory_android.cc
+++ b/chromium/base/memory/shared_memory_android.cc
@@ -37,6 +37,15 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
DLOG(ERROR) << "Error " << err << " when setting protection of ashmem";
return false;
}
+
+ // Android doesn't appear to have a way to drop write access on an ashmem
+ // segment for a single descriptor. http://crbug.com/320865
+ readonly_mapped_file_ = dup(mapped_file_);
+ if (-1 == readonly_mapped_file_) {
+ DPLOG(ERROR) << "dup() failed";
+ return false;
+ }
+
requested_size_ = options.size;
return true;
diff --git a/chromium/base/memory/shared_memory_nacl.cc b/chromium/base/memory/shared_memory_nacl.cc
index bc2a98dfdf6..93c10026191 100644
--- a/chromium/base/memory/shared_memory_nacl.cc
+++ b/chromium/base/memory/shared_memory_nacl.cc
@@ -140,7 +140,13 @@ void SharedMemory::Unlock() {
bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle *new_handle,
- bool close_self) {
+ bool close_self,
+ ShareMode share_mode) {
+ if (share_mode == SHARE_READONLY) {
+ // Untrusted code can't create descriptors or handles, which is needed to
+ // drop permissions.
+ return false;
+ }
const int new_fd = dup(mapped_file_);
if (new_fd < 0) {
DPLOG(ERROR) << "dup() failed.";
diff --git a/chromium/base/memory/shared_memory_posix.cc b/chromium/base/memory/shared_memory_posix.cc
index e6745ea3a7c..35d8c7d35df 100644
--- a/chromium/base/memory/shared_memory_posix.cc
+++ b/chromium/base/memory/shared_memory_posix.cc
@@ -30,20 +30,20 @@
#include "third_party/ashmem/ashmem.h"
#endif
+using file_util::ScopedFD;
+using file_util::ScopedFILE;
+
namespace base {
namespace {
-// Paranoia. Semaphores and shared memory segments should live in different
-// namespaces, but who knows what's out there.
-const char kSemaphoreSuffix[] = "-sem";
-
LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER;
}
SharedMemory::SharedMemory()
: mapped_file_(-1),
+ readonly_mapped_file_(-1),
inode_(0),
mapped_size_(0),
memory_(NULL),
@@ -53,6 +53,7 @@ SharedMemory::SharedMemory()
SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
: mapped_file_(handle.fd),
+ readonly_mapped_file_(-1),
inode_(0),
mapped_size_(0),
memory_(NULL),
@@ -69,6 +70,7 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
ProcessHandle process)
: mapped_file_(handle.fd),
+ readonly_mapped_file_(-1),
inode_(0),
mapped_size_(0),
memory_(NULL),
@@ -96,7 +98,7 @@ SharedMemoryHandle SharedMemory::NULLHandle() {
// static
void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
DCHECK_GE(handle.fd, 0);
- if (HANDLE_EINTR(close(handle.fd)) < 0)
+ if (close(handle.fd) < 0)
DPLOG(ERROR) << "close";
}
@@ -128,8 +130,10 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
// and be deleted before they ever make it out to disk.
base::ThreadRestrictions::ScopedAllowIO allow_io;
- FILE *fp;
+ ScopedFILE fp;
bool fix_size = true;
+ int readonly_fd_storage = -1;
+ ScopedFD readonly_fd(&readonly_fd_storage);
FilePath path;
if (options.name == NULL || options.name->empty()) {
@@ -137,12 +141,18 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
DCHECK(!options.open_existing);
// Q: Why not use the shm_open() etc. APIs?
// A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
- fp = file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable);
+ fp.reset(base::CreateAndOpenTemporaryShmemFile(&path, options.executable));
- // Deleting the file prevents anyone else from mapping it in (making it
- // private), and prevents the need for cleanup (once the last fd is closed,
- // it is truly freed).
if (fp) {
+ // 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();
+ }
+ // Deleting the file prevents anyone else from mapping it in (making it
+ // private), and prevents the need for cleanup (once the last fd is
+ // closed, it is truly freed).
if (unlink(path.value().c_str()))
PLOG(WARNING) << "unlink";
}
@@ -179,32 +189,35 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
sb.st_uid != effective_uid)) {
LOG(ERROR) <<
"Invalid owner when opening existing shared memory file.";
- HANDLE_EINTR(close(fd));
+ close(fd);
return false;
}
// An existing file was opened, so its size should not be fixed.
fix_size = false;
}
- fp = NULL;
+
+ // 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 (fd >= 0) {
// "a+" is always appropriate: if it's a new file, a+ is similar to w+.
- fp = fdopen(fd, "a+");
+ fp.reset(fdopen(fd, "a+"));
}
}
if (fp && fix_size) {
// Get current size.
struct stat stat;
- if (fstat(fileno(fp), &stat) != 0) {
- file_util::CloseFile(fp);
+ if (fstat(fileno(fp.get()), &stat) != 0)
return false;
- }
const size_t current_size = stat.st_size;
if (current_size != options.size) {
- if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) {
- file_util::CloseFile(fp);
+ if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
return false;
- }
}
requested_size_ = options.size;
}
@@ -225,7 +238,7 @@ bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
return false;
}
- return PrepareMapFile(fp);
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
}
// Our current implementation of shmem is with mmap()ing of files.
@@ -251,8 +264,14 @@ bool SharedMemory::Open(const std::string& name, bool read_only) {
read_only_ = read_only;
const char *mode = read_only ? "r" : "r+";
- FILE *fp = file_util::OpenFile(path, mode);
- return PrepareMapFile(fp);
+ 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) {
+ DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
+ }
+ return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
}
#endif // !defined(OS_ANDROID)
@@ -309,10 +328,15 @@ void SharedMemory::Close() {
Unmap();
if (mapped_file_ > 0) {
- if (HANDLE_EINTR(close(mapped_file_)) < 0)
+ if (close(mapped_file_) < 0)
PLOG(ERROR) << "close";
mapped_file_ = -1;
}
+ if (readonly_mapped_file_ > 0) {
+ if (close(readonly_mapped_file_) < 0)
+ PLOG(ERROR) << "close";
+ readonly_mapped_file_ = -1;
+ }
}
void SharedMemory::Lock() {
@@ -326,18 +350,28 @@ void SharedMemory::Unlock() {
}
#if !defined(OS_ANDROID)
-bool SharedMemory::PrepareMapFile(FILE *fp) {
+bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
DCHECK_EQ(-1, mapped_file_);
- if (fp == NULL) return false;
+ DCHECK_EQ(-1, readonly_mapped_file_);
+ if (fp == NULL || *readonly_fd < 0) return false;
// This function theoretically can block on the disk, but realistically
// the temporary files we create will just go into the buffer cache
// and be deleted before they ever make it out to disk.
base::ThreadRestrictions::ScopedAllowIO allow_io;
- file_util::ScopedFILE file_closer(fp);
+ 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;
+ }
- mapped_file_ = dup(fileno(fp));
+ mapped_file_ = dup(fileno(fp.get()));
if (mapped_file_ == -1) {
if (errno == EMFILE) {
LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
@@ -346,11 +380,8 @@ bool SharedMemory::PrepareMapFile(FILE *fp) {
NOTREACHED() << "Call to dup failed, errno=" << errno;
}
}
-
- struct stat st;
- if (fstat(mapped_file_, &st))
- NOTREACHED();
inode_ = st.st_ino;
+ readonly_mapped_file_ = *readonly_fd.release();
return true;
}
@@ -367,7 +398,7 @@ bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
DCHECK_EQ(std::string::npos, mem_name.find('\0'));
FilePath temp_dir;
- if (!file_util::GetShmemTempDir(&temp_dir, false))
+ if (!GetShmemTempDir(false, &temp_dir))
return false;
#if !defined(OS_MACOSX)
@@ -403,9 +434,23 @@ void SharedMemory::LockOrUnlockCommon(int function) {
}
bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
- SharedMemoryHandle *new_handle,
- bool close_self) {
- const int new_fd = dup(mapped_file_);
+ SharedMemoryHandle* new_handle,
+ bool close_self,
+ ShareMode share_mode) {
+ int handle_to_dup = -1;
+ switch(share_mode) {
+ case SHARE_CURRENT_MODE:
+ handle_to_dup = mapped_file_;
+ break;
+ case SHARE_READONLY:
+ // We could imagine re-opening the file from /dev/fd, but that can't make
+ // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
+ CHECK(readonly_mapped_file_ >= 0);
+ handle_to_dup = readonly_mapped_file_;
+ break;
+ }
+
+ const int new_fd = dup(handle_to_dup);
if (new_fd < 0) {
DPLOG(ERROR) << "dup() failed.";
return false;
diff --git a/chromium/base/memory/shared_memory_unittest.cc b/chromium/base/memory/shared_memory_unittest.cc
index 892fd7f1a59..f3c612f803d 100644
--- a/chromium/base/memory/shared_memory_unittest.cc
+++ b/chromium/base/memory/shared_memory_unittest.cc
@@ -20,14 +20,22 @@
#endif
#if defined(OS_POSIX)
+#include <errno.h>
+#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#endif
+#if defined(OS_WIN)
+#include "base/win/scoped_handle.h"
+#endif
+
static const int kNumThreads = 5;
+#if !defined(OS_IOS) // iOS does not allow multiple processes.
static const int kNumTasks = 5;
+#endif
namespace base {
@@ -361,6 +369,107 @@ TEST(SharedMemoryTest, AnonymousPrivate) {
}
}
+TEST(SharedMemoryTest, ShareReadOnly) {
+ StringPiece contents = "Hello World";
+
+ SharedMemory writable_shmem;
+ ASSERT_TRUE(writable_shmem.CreateAndMapAnonymous(contents.size()));
+ memcpy(writable_shmem.memory(), contents.data(), contents.size());
+ EXPECT_TRUE(writable_shmem.Unmap());
+
+ SharedMemoryHandle readonly_handle;
+ ASSERT_TRUE(writable_shmem.ShareReadOnlyToProcess(GetCurrentProcessHandle(),
+ &readonly_handle));
+ SharedMemory readonly_shmem(readonly_handle, /*readonly=*/true);
+
+ ASSERT_TRUE(readonly_shmem.Map(contents.size()));
+ EXPECT_EQ(contents,
+ StringPiece(static_cast<const char*>(readonly_shmem.memory()),
+ contents.size()));
+ EXPECT_TRUE(readonly_shmem.Unmap());
+
+ // Make sure the writable instance is still writable.
+ ASSERT_TRUE(writable_shmem.Map(contents.size()));
+ StringPiece new_contents = "Goodbye";
+ memcpy(writable_shmem.memory(), new_contents.data(), new_contents.size());
+ EXPECT_EQ(new_contents,
+ StringPiece(static_cast<const char*>(writable_shmem.memory()),
+ new_contents.size()));
+
+ // We'd like to check that if we send the read-only segment to another
+ // process, then that other process can't reopen it read/write. (Since that
+ // would be a security hole.) Setting up multiple processes is hard in a
+ // unittest, so this test checks that the *current* process can't reopen the
+ // segment read/write. I think the test here is stronger than we actually
+ // care about, but there's a remote possibility that sending a file over a
+ // pipe would transform it into read/write.
+ SharedMemoryHandle handle = readonly_shmem.handle();
+
+#if defined(OS_ANDROID)
+ // The "read-only" handle is still writable on Android:
+ // http://crbug.com/320865
+ (void)handle;
+#elif defined(OS_POSIX)
+ EXPECT_EQ(O_RDONLY, fcntl(handle.fd, F_GETFL) & O_ACCMODE)
+ << "The descriptor itself should be read-only.";
+
+ errno = 0;
+ void* writable = mmap(
+ NULL, contents.size(), PROT_READ | PROT_WRITE, MAP_SHARED, handle.fd, 0);
+ int mmap_errno = errno;
+ EXPECT_EQ(MAP_FAILED, writable)
+ << "It shouldn't be possible to re-mmap the descriptor writable.";
+ EXPECT_EQ(EACCES, mmap_errno) << strerror(mmap_errno);
+ if (writable != MAP_FAILED)
+ EXPECT_EQ(0, munmap(writable, readonly_shmem.mapped_size()));
+
+#elif defined(OS_WIN)
+ EXPECT_EQ(NULL, MapViewOfFile(handle, FILE_MAP_WRITE, 0, 0, 0))
+ << "Shouldn't be able to map memory writable.";
+
+ HANDLE temp_handle;
+ BOOL rv = ::DuplicateHandle(GetCurrentProcess(),
+ handle,
+ GetCurrentProcess,
+ &temp_handle,
+ FILE_MAP_ALL_ACCESS,
+ false,
+ 0);
+ EXPECT_EQ(FALSE, rv)
+ << "Shouldn't be able to duplicate the handle into a writable one.";
+ if (rv)
+ base::win::ScopedHandle writable_handle(temp_handle);
+#else
+#error Unexpected platform; write a test that tries to make 'handle' writable.
+#endif // defined(OS_POSIX) || defined(OS_WIN)
+}
+
+TEST(SharedMemoryTest, ShareToSelf) {
+ StringPiece contents = "Hello World";
+
+ SharedMemory shmem;
+ ASSERT_TRUE(shmem.CreateAndMapAnonymous(contents.size()));
+ memcpy(shmem.memory(), contents.data(), contents.size());
+ EXPECT_TRUE(shmem.Unmap());
+
+ SharedMemoryHandle shared_handle;
+ ASSERT_TRUE(shmem.ShareToProcess(GetCurrentProcessHandle(), &shared_handle));
+ SharedMemory shared(shared_handle, /*readonly=*/false);
+
+ ASSERT_TRUE(shared.Map(contents.size()));
+ EXPECT_EQ(
+ contents,
+ StringPiece(static_cast<const char*>(shared.memory()), contents.size()));
+
+ ASSERT_TRUE(shmem.ShareToProcess(GetCurrentProcessHandle(), &shared_handle));
+ SharedMemory readonly(shared_handle, /*readonly=*/true);
+
+ ASSERT_TRUE(readonly.Map(contents.size()));
+ EXPECT_EQ(contents,
+ StringPiece(static_cast<const char*>(readonly.memory()),
+ contents.size()));
+}
+
TEST(SharedMemoryTest, MapAt) {
ASSERT_TRUE(SysInfo::VMAllocationGranularity() >= sizeof(uint32));
const size_t kCount = SysInfo::VMAllocationGranularity();
diff --git a/chromium/base/memory/shared_memory_win.cc b/chromium/base/memory/shared_memory_win.cc
index 42e0b046b96..77741bc0d9d 100644
--- a/chromium/base/memory/shared_memory_win.cc
+++ b/chromium/base/memory/shared_memory_win.cc
@@ -60,8 +60,8 @@ SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
lock_(NULL) {
::DuplicateHandle(process, handle,
GetCurrentProcess(), &mapped_file_,
- STANDARD_RIGHTS_REQUIRED |
- (read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
+ read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
+ FILE_MAP_WRITE,
FALSE, 0);
}
@@ -147,8 +147,8 @@ bool SharedMemory::Open(const std::string& name, bool read_only) {
name_ = ASCIIToWide(name);
read_only_ = read_only;
mapped_file_ = OpenFileMapping(
- read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false,
- name_.empty() ? NULL : name_.c_str());
+ read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
+ false, name_.empty() ? NULL : name_.c_str());
if (mapped_file_ != NULL) {
// Note: size_ is not set in this case.
return true;
@@ -164,7 +164,8 @@ bool SharedMemory::MapAt(off_t offset, size_t bytes) {
return false;
memory_ = MapViewOfFile(mapped_file_,
- read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,
+ read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
+ FILE_MAP_WRITE,
static_cast<uint64>(offset) >> 32,
static_cast<DWORD>(offset),
bytes);
@@ -188,13 +189,14 @@ bool SharedMemory::Unmap() {
bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
SharedMemoryHandle *new_handle,
- bool close_self) {
+ bool close_self,
+ ShareMode share_mode) {
*new_handle = 0;
- DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ;
+ DWORD access = FILE_MAP_READ;
DWORD options = 0;
HANDLE mapped_file = mapped_file_;
HANDLE result;
- if (!read_only_)
+ if (share_mode == SHARE_CURRENT_MODE && !read_only_)
access |= FILE_MAP_WRITE;
if (close_self) {
// DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
diff --git a/chromium/base/message_loop/message_loop.cc b/chromium/base/message_loop/message_loop.cc
index 87a0b631735..3f9d01c8620 100644
--- a/chromium/base/message_loop/message_loop.cc
+++ b/chromium/base/message_loop/message_loop.cc
@@ -144,58 +144,23 @@ MessageLoop::MessageLoop(Type type)
#endif // OS_WIN
message_histogram_(NULL),
run_loop_(NULL) {
- DCHECK(!current()) << "should only have one message loop per thread";
- lazy_tls_ptr.Pointer()->Set(this);
+ Init();
- incoming_task_queue_ = new internal::IncomingTaskQueue(this);
- message_loop_proxy_ =
- new internal::MessageLoopProxyImpl(incoming_task_queue_);
- thread_task_runner_handle_.reset(
- new ThreadTaskRunnerHandle(message_loop_proxy_));
+ pump_.reset(CreateMessagePumpForType(type));
+}
-// TODO(rvargas): Get rid of the OS guards.
+MessageLoop::MessageLoop(scoped_ptr<MessagePump> pump)
+ : pump_(pump.Pass()),
+ type_(TYPE_CUSTOM),
+ exception_restoration_(false),
+ nestable_tasks_allowed_(true),
#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()
-#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()
-#else
-#error Not implemented
-#endif
-
- if (type_ == TYPE_UI) {
- if (message_pump_for_ui_factory_)
- pump_.reset(message_pump_for_ui_factory_());
- else
- pump_.reset(MESSAGE_PUMP_UI);
- } else if (type_ == TYPE_IO) {
- pump_.reset(MESSAGE_PUMP_IO);
-#if defined(TOOLKIT_GTK)
- } else if (type_ == TYPE_GPU) {
- pump_.reset(new MessagePumpX11());
-#endif
-#if defined(OS_ANDROID)
- } else if (type_ == TYPE_JAVA) {
- pump_.reset(MESSAGE_PUMP_UI);
-#endif
- } else {
- DCHECK_EQ(TYPE_DEFAULT, type_);
- pump_.reset(new MessagePumpDefault());
- }
+ os_modal_loop_(false),
+#endif // OS_WIN
+ message_histogram_(NULL),
+ run_loop_(NULL) {
+ DCHECK(pump_.get());
+ Init();
}
MessageLoop::~MessageLoop() {
@@ -257,6 +222,51 @@ bool MessageLoop::InitMessagePumpForUIFactory(MessagePumpFactory* factory) {
return true;
}
+// static
+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()
+#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()
+#else
+#error Not implemented
+#endif
+
+ if (type == MessageLoop::TYPE_UI) {
+ if (message_pump_for_ui_factory_)
+ return message_pump_for_ui_factory_();
+ 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
+#if defined(OS_ANDROID)
+ if (type == MessageLoop::TYPE_JAVA)
+ return MESSAGE_PUMP_UI;
+#endif
+ DCHECK_EQ(MessageLoop::TYPE_DEFAULT, type);
+ return new MessagePumpDefault();
+}
+
void MessageLoop::AddDestructionObserver(
DestructionObserver* destruction_observer) {
DCHECK_EQ(this, current());
@@ -348,13 +358,12 @@ Closure MessageLoop::QuitWhenIdleClosure() {
}
void MessageLoop::SetNestableTasksAllowed(bool allowed) {
- if (nestable_tasks_allowed_ != allowed) {
- nestable_tasks_allowed_ = allowed;
- if (!nestable_tasks_allowed_)
- return;
- // Start the native pump if we are not already pumping.
+ if (allowed) {
+ // Kick the native pump just in case we enter a OS-driven nested message
+ // loop.
pump_->ScheduleWork();
}
+ nestable_tasks_allowed_ = allowed;
}
bool MessageLoop::NestableTasksAllowed() const {
@@ -397,6 +406,17 @@ void MessageLoop::LockWaitUnLockForTesting(WaitableEvent* caller_wait,
//------------------------------------------------------------------------------
+void MessageLoop::Init() {
+ DCHECK(!current()) << "should only have one message loop per thread";
+ lazy_tls_ptr.Pointer()->Set(this);
+
+ incoming_task_queue_ = new internal::IncomingTaskQueue(this);
+ message_loop_proxy_ =
+ new internal::MessageLoopProxyImpl(incoming_task_queue_);
+ thread_task_runner_handle_.reset(
+ 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().
@@ -695,12 +715,6 @@ void MessageLoop::ReleaseSoonInternal(
//------------------------------------------------------------------------------
// MessageLoopForUI
-#if defined(OS_WIN)
-void MessageLoopForUI::DidProcessMessage(const MSG& message) {
- pump_win()->DidProcessMessage(message);
-}
-#endif // defined(OS_WIN)
-
#if defined(OS_ANDROID)
void MessageLoopForUI::Start() {
// No Histogram support for UI message loop as it is managed by Java side
diff --git a/chromium/base/message_loop/message_loop.h b/chromium/base/message_loop/message_loop.h
index 3520b72516f..e307d365a00 100644
--- a/chromium/base/message_loop/message_loop.h
+++ b/chromium/base/message_loop/message_loop.h
@@ -128,9 +128,13 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// TYPE_JAVA behaves in essence like TYPE_UI, except during construction
// where it does not use the main thread specific pump factory.
//
+ // TYPE_CUSTOM
+ // MessagePump was supplied to constructor.
+ //
enum Type {
TYPE_DEFAULT,
TYPE_UI,
+ TYPE_CUSTOM,
#if defined(TOOLKIT_GTK)
TYPE_GPU,
#endif
@@ -143,6 +147,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// Normally, it is not necessary to instantiate a MessageLoop. Instead, it
// is typical to make use of the current thread's MessageLoop instance.
explicit MessageLoop(Type type = TYPE_DEFAULT);
+ // Creates a TYPE_CUSTOM MessageLoop with the supplied MessagePump, which must
+ // be non-NULL.
+ explicit MessageLoop(scoped_ptr<base::MessagePump> pump);
virtual ~MessageLoop();
// Returns the MessageLoop object for the current thread, or null if none.
@@ -156,6 +163,12 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
// was successfully registered.
static bool InitMessagePumpForUIFactory(MessagePumpFactory* factory);
+ // 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);
+
// 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
@@ -442,6 +455,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
friend class internal::IncomingTaskQueue;
friend class RunLoop;
+ // 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()
@@ -504,7 +520,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate {
virtual void GetQueueingInformation(size_t* queue_size,
TimeDelta* queueing_delay) OVERRIDE;
- Type type_;
+ const Type type_;
// A list of tasks that need to be processed by this instance. Note that
// this queue is only accessed (push/pop) by our current thread.
@@ -586,10 +602,6 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop {
return static_cast<MessageLoopForUI*>(loop);
}
-#if defined(OS_WIN)
- void DidProcessMessage(const MSG& message);
-#endif // defined(OS_WIN)
-
#if defined(OS_IOS)
// On iOS, the main message loop cannot be Run(). Instead call Attach(),
// which connects this MessageLoop to the UI thread's CFRunLoop and allows
diff --git a/chromium/base/message_loop/message_loop_test.cc b/chromium/base/message_loop/message_loop_test.cc
new file mode 100644
index 00000000000..9ac5b72d374
--- /dev/null
+++ b/chromium/base/message_loop/message_loop_test.cc
@@ -0,0 +1,1069 @@
+// 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/message_loop/message_loop_test.h"
+
+#include "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/run_loop.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+
+namespace base {
+namespace test {
+
+namespace {
+
+class Foo : public RefCounted<Foo> {
+ public:
+ Foo() : test_count_(0) {
+ }
+
+ void Test0() {
+ ++test_count_;
+ }
+
+ void Test1ConstRef(const std::string& a) {
+ ++test_count_;
+ result_.append(a);
+ }
+
+ void Test1Ptr(std::string* a) {
+ ++test_count_;
+ result_.append(*a);
+ }
+
+ void Test1Int(int a) {
+ test_count_ += a;
+ }
+
+ void Test2Ptr(std::string* a, std::string* b) {
+ ++test_count_;
+ result_.append(*a);
+ result_.append(*b);
+ }
+
+ void Test2Mixed(const std::string& a, std::string* b) {
+ ++test_count_;
+ result_.append(a);
+ result_.append(*b);
+ }
+
+ int test_count() const { return test_count_; }
+ const std::string& result() const { return result_; }
+
+ private:
+ friend class RefCounted<Foo>;
+
+ ~Foo() {}
+
+ int test_count_;
+ std::string result_;
+
+ DISALLOW_COPY_AND_ASSIGN(Foo);
+};
+
+// This function runs slowly to simulate a large amount of work being done.
+void SlowFunc(TimeDelta pause, int* quit_counter) {
+ PlatformThread::Sleep(pause);
+ if (--(*quit_counter) == 0)
+ MessageLoop::current()->QuitWhenIdle();
+}
+
+// This function records the time when Run was called in a Time object, which is
+// useful for building a variety of MessageLoop tests.
+// TODO(sky): remove?
+void RecordRunTimeFunc(Time* run_time, int* quit_counter) {
+ *run_time = Time::Now();
+
+ // Cause our Run function to take some time to execute. As a result we can
+ // count on subsequent RecordRunTimeFunc()s running at a future time,
+ // without worry about the resolution of our system clock being an issue.
+ SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
+}
+
+} // namespace
+
+void RunTest_PostTask(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));
+
+ // 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);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ EXPECT_EQ(foo->test_count(), 105);
+ EXPECT_EQ(foo->result(), "abacad");
+}
+
+void RunTest_PostDelayedTask_Basic(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that PostDelayedTask results in a delayed task.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 1;
+ Time run_time;
+
+ loop.PostDelayedTask(
+ FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ kDelay);
+
+ Time time_before_run = Time::Now();
+ loop.Run();
+ Time time_after_run = Time::Now();
+
+ EXPECT_EQ(0, num_tasks);
+ EXPECT_LT(kDelay, time_after_run - time_before_run);
+}
+
+void RunTest_PostDelayedTask_InDelayOrder(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that two tasks with different delays run in the right order.
+ int num_tasks = 2;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromMilliseconds(200));
+ // If we get a large pause in execution (due to a context switch) here, this
+ // test could fail.
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 < run_time1);
+}
+
+void RunTest_PostDelayedTask_InPostOrder(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that two tasks with the same delay run in the order in which they
+ // were posted.
+ //
+ // NOTE: This is actually an approximate test since the API only takes a
+ // "delay" parameter, so we are not exactly simulating two tasks that get
+ // posted at the exact same time. It would be nice if the API allowed us to
+ // specify the desired run time.
+
+ const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
+
+ int num_tasks = 2;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time1 < run_time2);
+}
+
+void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that a delayed task still runs after a normal tasks even if the
+ // normal tasks take a long time to run.
+
+ const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
+
+ int num_tasks = 2;
+ Time run_time;
+
+ loop.PostTask(FROM_HERE, Bind(&SlowFunc, kPause, &num_tasks));
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ Time time_before_run = Time::Now();
+ loop.Run();
+ Time time_after_run = Time::Now();
+
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_LT(kPause, time_after_run - time_before_run);
+}
+
+void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that a delayed task still runs after a pile of normal tasks. The key
+ // difference between this test and the previous one is that here we return
+ // the MessageLoop a lot so we give the MessageLoop plenty of opportunities
+ // to maybe run the delayed task. It should know not to do so until the
+ // delayed task's delay has passed.
+
+ int num_tasks = 11;
+ Time run_time1, run_time2;
+
+ // Clutter the ML with tasks.
+ for (int i = 1; i < num_tasks; ++i)
+ loop.PostTask(FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time1, &num_tasks));
+
+ loop.PostDelayedTask(
+ FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(1));
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ EXPECT_TRUE(run_time2 > run_time1);
+}
+
+void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ // Test that the interval of the timer, used to run the next delayed task, is
+ // set to a value corresponding to when the next delayed task should run.
+
+ // By setting num_tasks to 1, we ensure that the first task to run causes the
+ // run loop to exit.
+ int num_tasks = 1;
+ Time run_time1, run_time2;
+
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
+ TimeDelta::FromSeconds(1000));
+ loop.PostDelayedTask(
+ FROM_HERE,
+ Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
+ TimeDelta::FromMilliseconds(10));
+
+ Time start_time = Time::Now();
+
+ loop.Run();
+ EXPECT_EQ(0, num_tasks);
+
+ // Ensure that we ran in far less time than the slower timer.
+ TimeDelta total_time = Time::Now() - start_time;
+ EXPECT_GT(5000, total_time.InMilliseconds());
+
+ // In case both timers somehow run at nearly the same time, sleep a little
+ // and then run all pending to force them both to have run. This is just
+ // encouraging flakiness if there is any.
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
+ RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(run_time1.is_null());
+ EXPECT_FALSE(run_time2.is_null());
+}
+
+// This is used to inject a test point for recording the destructor calls for
+// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we
+// are trying to hook the actual destruction, which is not a common operation.
+class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
+ public:
+ RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
+ : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {
+ }
+ void Run() {}
+
+ private:
+ friend class RefCounted<RecordDeletionProbe>;
+
+ ~RecordDeletionProbe() {
+ *was_deleted_ = true;
+ if (post_on_delete_.get())
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_.get()));
+ }
+
+ scoped_refptr<RecordDeletionProbe> post_on_delete_;
+ bool* was_deleted_;
+};
+
+void RunTest_EnsureDeletion(MessagePumpFactory factory) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+ loop.PostTask(
+ FROM_HERE, Bind(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &a_was_deleted)));
+ // TODO(ajwong): Do we really need 1000ms here?
+ loop.PostDelayedTask(
+ FROM_HERE, Bind(&RecordDeletionProbe::Run,
+ new RecordDeletionProbe(NULL, &b_was_deleted)),
+ TimeDelta::FromMilliseconds(1000));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+}
+
+void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory) {
+ bool a_was_deleted = false;
+ bool b_was_deleted = false;
+ bool c_was_deleted = false;
+ {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+ // The scoped_refptr for each of the below is held either by the chained
+ // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback.
+ RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
+ RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
+ RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
+ loop.PostTask(FROM_HERE, Bind(&RecordDeletionProbe::Run, c));
+ }
+ EXPECT_TRUE(a_was_deleted);
+ EXPECT_TRUE(b_was_deleted);
+ EXPECT_TRUE(c_was_deleted);
+}
+
+void NestingFunc(int* depth) {
+ if (*depth > 0) {
+ *depth -= 1;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&NestingFunc, depth));
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ }
+ MessageLoop::current()->QuitWhenIdle();
+}
+
+void RunTest_Nesting(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ int depth = 100;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&NestingFunc, &depth));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(depth, 0);
+}
+
+enum TaskType {
+ MESSAGEBOX,
+ ENDDIALOG,
+ RECURSIVE,
+ TIMEDMESSAGELOOP,
+ QUITMESSAGELOOP,
+ ORDERED,
+ PUMPS,
+ SLEEP,
+ RUNS,
+};
+
+struct TaskItem {
+ TaskItem(TaskType t, int c, bool s)
+ : type(t),
+ cookie(c),
+ start(s) {
+ }
+
+ TaskType type;
+ int cookie;
+ bool start;
+
+ bool operator == (const TaskItem& other) const {
+ return type == other.type && cookie == other.cookie && start == other.start;
+ }
+};
+
+std::ostream& operator <<(std::ostream& os, TaskType type) {
+ switch (type) {
+ case MESSAGEBOX: os << "MESSAGEBOX"; break;
+ case ENDDIALOG: os << "ENDDIALOG"; break;
+ case RECURSIVE: os << "RECURSIVE"; break;
+ case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break;
+ case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break;
+ case ORDERED: os << "ORDERED"; break;
+ case PUMPS: os << "PUMPS"; break;
+ case SLEEP: os << "SLEEP"; break;
+ default:
+ NOTREACHED();
+ os << "Unknown TaskType";
+ break;
+ }
+ return os;
+}
+
+std::ostream& operator <<(std::ostream& os, const TaskItem& item) {
+ if (item.start)
+ return os << item.type << " " << item.cookie << " starts";
+ else
+ return os << item.type << " " << item.cookie << " ends";
+}
+
+class TaskList {
+ public:
+ void RecordStart(TaskType type, int cookie) {
+ TaskItem item(type, cookie, true);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ void RecordEnd(TaskType type, int cookie) {
+ TaskItem item(type, cookie, false);
+ DVLOG(1) << item;
+ task_list_.push_back(item);
+ }
+
+ size_t Size() {
+ return task_list_.size();
+ }
+
+ TaskItem Get(int n) {
+ return task_list_[n];
+ }
+
+ private:
+ std::vector<TaskItem> task_list_;
+};
+
+void RecursiveFunc(TaskList* order, int cookie, int depth,
+ bool is_reentrant) {
+ order->RecordStart(RECURSIVE, cookie);
+ if (depth > 0) {
+ if (is_reentrant)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&RecursiveFunc, order, cookie, depth - 1, is_reentrant));
+ }
+ order->RecordEnd(RECURSIVE, cookie);
+}
+
+void QuitFunc(TaskList* order, int cookie) {
+ order->RecordStart(QUITMESSAGELOOP, cookie);
+ MessageLoop::current()->QuitWhenIdle();
+ order->RecordEnd(QUITMESSAGELOOP, cookie);
+}
+void RunTest_RecursiveDenial1(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&RecursiveFunc, &order, 1, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&RecursiveFunc, &order, 2, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&QuitFunc, &order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+void RecursiveSlowFunc(TaskList* order, int cookie, int depth,
+ bool is_reentrant) {
+ RecursiveFunc(order, cookie, depth, is_reentrant);
+ PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
+}
+
+void OrderedFunc(TaskList* order, int cookie) {
+ order->RecordStart(ORDERED, cookie);
+ order->RecordEnd(ORDERED, cookie);
+}
+
+void RunTest_RecursiveDenial3(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 3),
+ TimeDelta::FromMilliseconds(5));
+ MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ Bind(&QuitFunc, &order, 4),
+ TimeDelta::FromMilliseconds(5));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(16U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false));
+}
+
+void RunTest_RecursiveSupport1(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&QuitFunc, &order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(14U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
+}
+
+// Tests that non nestable tasks run in FIFO if there are no nested loops.
+void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&QuitFunc, &order, 3));
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(6U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
+}
+
+void FuncThatPumps(TaskList* order, int cookie) {
+ order->RecordStart(PUMPS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ RunLoop().RunUntilIdle();
+ }
+ order->RecordEnd(PUMPS, cookie);
+}
+
+void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
+ order->RecordStart(SLEEP, cookie);
+ PlatformThread::Sleep(delay);
+ order->RecordEnd(SLEEP, cookie);
+}
+
+// Tests that non nestable tasks don't run when there's code in the call stack.
+void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory,
+ bool use_delayed) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&FuncThatPumps, &order, 1));
+ if (use_delayed) {
+ MessageLoop::current()->PostNonNestableDelayedTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 2),
+ TimeDelta::FromMilliseconds(1));
+ } else {
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&OrderedFunc, &order, 2));
+ }
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&OrderedFunc, &order, 5));
+ if (use_delayed) {
+ MessageLoop::current()->PostNonNestableDelayedTask(
+ FROM_HERE,
+ Bind(&QuitFunc, &order, 6),
+ TimeDelta::FromMilliseconds(2));
+ } else {
+ MessageLoop::current()->PostNonNestableTask(
+ FROM_HERE,
+ Bind(&QuitFunc, &order, 6));
+ }
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(12U, order.Size());
+ EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
+ EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
+ EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
+ EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
+ EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
+ EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
+}
+
+void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) {
+ order->RecordStart(RUNS, cookie);
+ {
+ MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
+ run_loop->Run();
+ }
+ order->RecordEnd(RUNS, cookie);
+}
+
+void FuncThatQuitsNow() {
+ MessageLoop::current()->QuitNow();
+}
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_QuitNow(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 4)); // never runs
+
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(6U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitTop(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitNested(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitBogus(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_run_loop;
+ RunLoop bogus_run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, bogus_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_run_loop.QuitClosure());
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(4U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
+void RunTest_RunLoopQuitDeep(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop outer_run_loop;
+ RunLoop nested_loop1;
+ RunLoop nested_loop2;
+ RunLoop nested_loop3;
+ RunLoop nested_loop4;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 5));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, outer_run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 6));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop1.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 7));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop2.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 8));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop3.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 9));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, nested_loop4.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 10));
+
+ outer_run_loop.Run();
+
+ ASSERT_EQ(18U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works before RunWithID.
+void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ run_loop.Quit();
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 1)); // never runs
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(0U, order.Size());
+}
+
+// Tests RunLoopQuit works during RunWithID.
+void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 1));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, run_loop.QuitClosure());
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2)); // never runs
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
+
+ run_loop.Run();
+
+ ASSERT_EQ(2U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+// Tests RunLoopQuit works after RunWithID.
+void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory) {
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+
+ TaskList order;
+
+ RunLoop run_loop;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 2));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 3));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, run_loop.QuitClosure()); // has no affect
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&OrderedFunc, &order, 4));
+ MessageLoop::current()->PostTask(
+ FROM_HERE, Bind(&FuncThatQuitsNow));
+
+ RunLoop outer_run_loop;
+ outer_run_loop.Run();
+
+ ASSERT_EQ(8U, order.Size());
+ int task_index = 0;
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
+ EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
+ EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
+}
+
+void PostNTasksThenQuit(int posts_remaining) {
+ if (posts_remaining > 1) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ Bind(&PostNTasksThenQuit, posts_remaining - 1));
+ } else {
+ MessageLoop::current()->QuitWhenIdle();
+ }
+}
+
+// There was a bug in the MessagePumpGLib where posting tasks recursively
+// caused the message loop to hang, due to the buffer of the internal pipe
+// becoming full. Test all MessageLoop types to ensure this issue does not
+// exist in other MessagePumps.
+//
+// On Linux, the pipe buffer size is 64KiB by default. The bug caused one
+// byte accumulated in the pipe per two posts, so we should repeat 128K
+// times to reproduce the bug.
+void RunTest_RecursivePosts(MessagePumpFactory factory) {
+ const int kNumTimes = 1 << 17;
+ scoped_ptr<MessagePump> pump(factory());
+ MessageLoop loop(pump.Pass());
+ loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, kNumTimes));
+ loop.Run();
+}
+
+} // namespace test
+} // namespace base
diff --git a/chromium/base/message_loop/message_loop_test.h b/chromium/base/message_loop/message_loop_test.h
new file mode 100644
index 00000000000..5d1a4f5ac27
--- /dev/null
+++ b/chromium/base/message_loop/message_loop_test.h
@@ -0,0 +1,137 @@
+// 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_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
+#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
+
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This file consists of tests meant to exercise the combination of MessageLoop
+// and MessagePump. To use these define the macro RUN_MESSAGE_LOOP_TESTS using
+// an ID appropriate for your MessagePump, eg
+// RUN_MESSAGE_LOOP_TESTS(UI, factory). Factory is a function called to create
+// the MessagePump.
+namespace base {
+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);
+void RunTest_PostDelayedTask_InPostOrder_2(MessagePumpFactory factory);
+void RunTest_PostDelayedTask_InPostOrder_3(MessagePumpFactory factory);
+void RunTest_PostDelayedTask_SharedTimer(MessagePumpFactory factory);
+void RunTest_EnsureDeletion(MessagePumpFactory factory);
+void RunTest_EnsureDeletion_Chain(MessagePumpFactory factory);
+void RunTest_Nesting(MessagePumpFactory factory);
+void RunTest_RecursiveDenial1(MessagePumpFactory factory);
+void RunTest_RecursiveDenial3(MessagePumpFactory factory);
+void RunTest_RecursiveSupport1(MessagePumpFactory factory);
+void RunTest_NonNestableWithNoNesting(MessagePumpFactory factory);
+void RunTest_NonNestableInNestedLoop(MessagePumpFactory factory,
+ bool use_delayed);
+void RunTest_QuitNow(MessagePumpFactory factory);
+void RunTest_RunLoopQuitTop(MessagePumpFactory factory);
+void RunTest_RunLoopQuitNested(MessagePumpFactory factory);
+void RunTest_RunLoopQuitBogus(MessagePumpFactory factory);
+void RunTest_RunLoopQuitDeep(MessagePumpFactory factory);
+void RunTest_RunLoopQuitOrderBefore(MessagePumpFactory factory);
+void RunTest_RunLoopQuitOrderDuring(MessagePumpFactory factory);
+void RunTest_RunLoopQuitOrderAfter(MessagePumpFactory factory);
+void RunTest_RecursivePosts(MessagePumpFactory factory);
+
+} // namespace test
+} // namespace base
+
+#define RUN_MESSAGE_LOOP_TESTS(id, 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); \
+ } \
+ TEST(MessageLoopTestType##id, PostDelayedTask_InDelayOrder) { \
+ base::test::RunTest_PostDelayedTask_InDelayOrder(factory); \
+ } \
+ TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder) { \
+ base::test::RunTest_PostDelayedTask_InPostOrder(factory); \
+ } \
+ TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_2) { \
+ base::test::RunTest_PostDelayedTask_InPostOrder_2(factory); \
+ } \
+ TEST(MessageLoopTestType##id, PostDelayedTask_InPostOrder_3) { \
+ base::test::RunTest_PostDelayedTask_InPostOrder_3(factory); \
+ } \
+ TEST(MessageLoopTestType##id, PostDelayedTask_SharedTimer) { \
+ base::test::RunTest_PostDelayedTask_SharedTimer(factory); \
+ } \
+ /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \
+ /* destructor. */ \
+ /* Fails, http://crbug.com/50272. */ \
+ TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion) { \
+ base::test::RunTest_EnsureDeletion(factory); \
+ } \
+ /* TODO(darin): MessageLoop does not support deleting all tasks in the */ \
+ /* destructor. */ \
+ /* Fails, http://crbug.com/50272. */ \
+ TEST(MessageLoopTestType##id, DISABLED_EnsureDeletion_Chain) { \
+ base::test::RunTest_EnsureDeletion_Chain(factory); \
+ } \
+ TEST(MessageLoopTestType##id, Nesting) { \
+ base::test::RunTest_Nesting(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RecursiveDenial1) { \
+ base::test::RunTest_RecursiveDenial1(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RecursiveDenial3) { \
+ base::test::RunTest_RecursiveDenial3(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RecursiveSupport1) { \
+ base::test::RunTest_RecursiveSupport1(factory); \
+ } \
+ TEST(MessageLoopTestType##id, NonNestableWithNoNesting) { \
+ base::test::RunTest_NonNestableWithNoNesting(factory); \
+ } \
+ TEST(MessageLoopTestType##id, NonNestableInNestedLoop) { \
+ base::test::RunTest_NonNestableInNestedLoop(factory, false); \
+ } \
+ TEST(MessageLoopTestType##id, NonNestableDelayedInNestedLoop) { \
+ base::test::RunTest_NonNestableInNestedLoop(factory, true); \
+ } \
+ TEST(MessageLoopTestType##id, QuitNow) { \
+ base::test::RunTest_QuitNow(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitTop) { \
+ base::test::RunTest_RunLoopQuitTop(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitNested) { \
+ base::test::RunTest_RunLoopQuitNested(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitBogus) { \
+ base::test::RunTest_RunLoopQuitBogus(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitDeep) { \
+ base::test::RunTest_RunLoopQuitDeep(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitOrderBefore) { \
+ base::test::RunTest_RunLoopQuitOrderBefore(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitOrderDuring) { \
+ base::test::RunTest_RunLoopQuitOrderDuring(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RunLoopQuitOrderAfter) { \
+ base::test::RunTest_RunLoopQuitOrderAfter(factory); \
+ } \
+ TEST(MessageLoopTestType##id, RecursivePosts) { \
+ base::test::RunTest_RecursivePosts(factory); \
+ } \
+
+#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_TEST_H_
diff --git a/chromium/base/message_loop/message_loop_unittest.cc b/chromium/base/message_loop/message_loop_unittest.cc
index ab05b3cb5ab..e6d25ecef82 100644
--- a/chromium/base/message_loop/message_loop_unittest.cc
+++ b/chromium/base/message_loop/message_loop_unittest.cc
@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy_impl.h"
+#include "base/message_loop/message_loop_test.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
@@ -22,6 +23,8 @@
#if defined(OS_WIN)
#include "base/message_loop/message_pump_win.h"
+#include "base/process/memory.h"
+#include "base/strings/string16.h"
#include "base/win/scoped_handle.h"
#endif
@@ -32,6 +35,18 @@ namespace base {
namespace {
+MessagePump* TypeDefaultMessagePumpFactory() {
+ return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_DEFAULT);
+}
+
+MessagePump* TypeIOMessagePumpFactory() {
+ return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_IO);
+}
+
+MessagePump* TypeUIMessagePumpFactory() {
+ return MessageLoop::CreateMessagePumpForType(MessageLoop::TYPE_UI);
+}
+
class Foo : public RefCounted<Foo> {
public:
Foo() : test_count_(0) {
@@ -79,88 +94,7 @@ class Foo : public RefCounted<Foo> {
std::string result_;
};
-void RunTest_PostTask(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Add tests to message loop
- scoped_refptr<Foo> foo(new Foo());
- std::string a("a"), b("b"), c("c"), d("d");
- MessageLoop::current()->PostTask(FROM_HERE, 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));
-
- // 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(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Add tests to message loop
- scoped_refptr<Foo> foo(new Foo());
- std::string a("a"), b("b"), c("c"), d("d");
- MessageLoop::current()->PostTask(FROM_HERE, 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);
- MessageLoop::current()->Run();
- MessageLoop::current()->set_exception_restoration(false);
-
- EXPECT_EQ(foo->test_count(), 105);
- EXPECT_EQ(foo->result(), "abacad");
-}
+#if defined(OS_WIN)
// This function runs slowly to simulate a large amount of work being done.
static void SlowFunc(TimeDelta pause, int* quit_counter) {
@@ -180,180 +114,6 @@ static void RecordRunTimeFunc(Time* run_time, int* quit_counter) {
SlowFunc(TimeDelta::FromMilliseconds(10), quit_counter);
}
-void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that PostDelayedTask results in a delayed task.
-
- const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
-
- int num_tasks = 1;
- Time run_time;
-
- loop.PostDelayedTask(
- FROM_HERE, Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
- kDelay);
-
- Time time_before_run = Time::Now();
- loop.Run();
- Time time_after_run = Time::Now();
-
- EXPECT_EQ(0, num_tasks);
- EXPECT_LT(kDelay, time_after_run - time_before_run);
-}
-
-void RunTest_PostDelayedTask_InDelayOrder(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that two tasks with different delays run in the right order.
- int num_tasks = 2;
- Time run_time1, run_time2;
-
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
- TimeDelta::FromMilliseconds(200));
- // If we get a large pause in execution (due to a context switch) here, this
- // test could fail.
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- loop.Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time2 < run_time1);
-}
-
-void RunTest_PostDelayedTask_InPostOrder(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that two tasks with the same delay run in the order in which they
- // were posted.
- //
- // NOTE: This is actually an approximate test since the API only takes a
- // "delay" parameter, so we are not exactly simulating two tasks that get
- // posted at the exact same time. It would be nice if the API allowed us to
- // specify the desired run time.
-
- const TimeDelta kDelay = TimeDelta::FromMilliseconds(100);
-
- int num_tasks = 2;
- Time run_time1, run_time2;
-
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time1, &num_tasks), kDelay);
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time2, &num_tasks), kDelay);
-
- loop.Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time1 < run_time2);
-}
-
-void RunTest_PostDelayedTask_InPostOrder_2(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that a delayed task still runs after a normal tasks even if the
- // normal tasks take a long time to run.
-
- const TimeDelta kPause = TimeDelta::FromMilliseconds(50);
-
- int num_tasks = 2;
- Time run_time;
-
- loop.PostTask(FROM_HERE, Bind(&SlowFunc, kPause, &num_tasks));
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- Time time_before_run = Time::Now();
- loop.Run();
- Time time_after_run = Time::Now();
-
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_LT(kPause, time_after_run - time_before_run);
-}
-
-void RunTest_PostDelayedTask_InPostOrder_3(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that a delayed task still runs after a pile of normal tasks. The key
- // difference between this test and the previous one is that here we return
- // the MessageLoop a lot so we give the MessageLoop plenty of opportunities
- // to maybe run the delayed task. It should know not to do so until the
- // delayed task's delay has passed.
-
- int num_tasks = 11;
- Time run_time1, run_time2;
-
- // Clutter the ML with tasks.
- for (int i = 1; i < num_tasks; ++i)
- loop.PostTask(FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time1, &num_tasks));
-
- loop.PostDelayedTask(
- FROM_HERE, Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(1));
-
- loop.Run();
- EXPECT_EQ(0, num_tasks);
-
- EXPECT_TRUE(run_time2 > run_time1);
-}
-
-void RunTest_PostDelayedTask_SharedTimer(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- // Test that the interval of the timer, used to run the next delayed task, is
- // set to a value corresponding to when the next delayed task should run.
-
- // By setting num_tasks to 1, we ensure that the first task to run causes the
- // run loop to exit.
- int num_tasks = 1;
- Time run_time1, run_time2;
-
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time1, &num_tasks),
- TimeDelta::FromSeconds(1000));
- loop.PostDelayedTask(
- FROM_HERE,
- Bind(&RecordRunTimeFunc, &run_time2, &num_tasks),
- TimeDelta::FromMilliseconds(10));
-
- Time start_time = Time::Now();
-
- loop.Run();
- EXPECT_EQ(0, num_tasks);
-
- // Ensure that we ran in far less time than the slower timer.
- TimeDelta total_time = Time::Now() - start_time;
- EXPECT_GT(5000, total_time.InMilliseconds());
-
- // In case both timers somehow run at nearly the same time, sleep a little
- // and then run all pending to force them both to have run. This is just
- // encouraging flakiness if there is any.
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(100));
- RunLoop().RunUntilIdle();
-
- EXPECT_TRUE(run_time1.is_null());
- EXPECT_FALSE(run_time2.is_null());
-}
-
-#if defined(OS_WIN)
-
void SubPumpFunc() {
MessageLoop::current()->SetNestableTasksAllowed(true);
MSG msg;
@@ -407,82 +167,6 @@ void RunTest_PostDelayedTask_SharedTimer_SubPump() {
EXPECT_TRUE(run_time.is_null());
}
-#endif // defined(OS_WIN)
-
-// This is used to inject a test point for recording the destructor calls for
-// Closure objects send to MessageLoop::PostTask(). It is awkward usage since we
-// are trying to hook the actual destruction, which is not a common operation.
-class RecordDeletionProbe : public RefCounted<RecordDeletionProbe> {
- public:
- RecordDeletionProbe(RecordDeletionProbe* post_on_delete, bool* was_deleted)
- : post_on_delete_(post_on_delete), was_deleted_(was_deleted) {
- }
- void Run() {}
-
- private:
- friend class RefCounted<RecordDeletionProbe>;
-
- ~RecordDeletionProbe() {
- *was_deleted_ = true;
- if (post_on_delete_.get())
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&RecordDeletionProbe::Run, post_on_delete_.get()));
- }
-
- scoped_refptr<RecordDeletionProbe> post_on_delete_;
- bool* was_deleted_;
-};
-
-void RunTest_EnsureDeletion(MessageLoop::Type message_loop_type) {
- bool a_was_deleted = false;
- bool b_was_deleted = false;
- {
- MessageLoop loop(message_loop_type);
- loop.PostTask(
- FROM_HERE, Bind(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(NULL, &a_was_deleted)));
- // TODO(ajwong): Do we really need 1000ms here?
- loop.PostDelayedTask(
- FROM_HERE, Bind(&RecordDeletionProbe::Run,
- new RecordDeletionProbe(NULL, &b_was_deleted)),
- TimeDelta::FromMilliseconds(1000));
- }
- EXPECT_TRUE(a_was_deleted);
- EXPECT_TRUE(b_was_deleted);
-}
-
-void RunTest_EnsureDeletion_Chain(MessageLoop::Type message_loop_type) {
- bool a_was_deleted = false;
- bool b_was_deleted = false;
- bool c_was_deleted = false;
- {
- MessageLoop loop(message_loop_type);
- // The scoped_refptr for each of the below is held either by the chained
- // RecordDeletionProbe, or the bound RecordDeletionProbe::Run() callback.
- RecordDeletionProbe* a = new RecordDeletionProbe(NULL, &a_was_deleted);
- RecordDeletionProbe* b = new RecordDeletionProbe(a, &b_was_deleted);
- RecordDeletionProbe* c = new RecordDeletionProbe(b, &c_was_deleted);
- loop.PostTask(FROM_HERE, Bind(&RecordDeletionProbe::Run, c));
- }
- EXPECT_TRUE(a_was_deleted);
- EXPECT_TRUE(b_was_deleted);
- EXPECT_TRUE(c_was_deleted);
-}
-
-void NestingFunc(int* depth) {
- if (*depth > 0) {
- *depth -= 1;
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&NestingFunc, depth));
-
- MessageLoop::current()->SetNestableTasksAllowed(true);
- MessageLoop::current()->Run();
- }
- MessageLoop::current()->QuitWhenIdle();
-}
-
-#if defined(OS_WIN)
-
LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
ADD_FAILURE() << "bad exception handler";
::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
@@ -597,19 +281,7 @@ void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) {
::SetUnhandledExceptionFilter(old_SEH_filter);
}
-#endif // defined(OS_WIN)
-
-void RunTest_Nesting(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- int depth = 100;
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&NestingFunc, &depth));
- MessageLoop::current()->Run();
- EXPECT_EQ(depth, 0);
-}
-
-const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
+const wchar_t kMessageBoxTitle[] = L"MessageLoop Unit Test";
enum TaskType {
MESSAGEBOX,
@@ -691,14 +363,6 @@ class TaskList {
std::vector<TaskItem> task_list_;
};
-// Saves the order the tasks ran.
-void OrderedFunc(TaskList* order, int cookie) {
- order->RecordStart(ORDERED, cookie);
- order->RecordEnd(ORDERED, cookie);
-}
-
-#if defined(OS_WIN)
-
// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
// common controls (like OpenFile) and StartDoc printing function can cause
// implicit message loops.
@@ -722,8 +386,6 @@ void EndDialogFunc(TaskList* order, int cookie) {
}
}
-#endif // defined(OS_WIN)
-
void RecursiveFunc(TaskList* order, int cookie, int depth,
bool is_reentrant) {
order->RecordStart(RECURSIVE, cookie);
@@ -737,25 +399,12 @@ void RecursiveFunc(TaskList* order, int cookie, int depth,
order->RecordEnd(RECURSIVE, cookie);
}
-void RecursiveSlowFunc(TaskList* order, int cookie, int depth,
- bool is_reentrant) {
- RecursiveFunc(order, cookie, depth, is_reentrant);
- PlatformThread::Sleep(TimeDelta::FromMilliseconds(10));
-}
-
void QuitFunc(TaskList* order, int cookie) {
order->RecordStart(QUITMESSAGELOOP, cookie);
MessageLoop::current()->QuitWhenIdle();
order->RecordEnd(QUITMESSAGELOOP, cookie);
}
-void SleepFunc(TaskList* order, int cookie, TimeDelta delay) {
- order->RecordStart(SLEEP, cookie);
- PlatformThread::Sleep(delay);
- order->RecordEnd(SLEEP, cookie);
-}
-
-#if defined(OS_WIN)
void RecursiveFuncWin(MessageLoop* target,
HANDLE event,
bool expect_window,
@@ -801,115 +450,6 @@ void RecursiveFuncWin(MessageLoop* target,
}
}
-#endif // defined(OS_WIN)
-
-void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
- TaskList order;
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&RecursiveFunc, &order, 1, 2, false));
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&RecursiveFunc, &order, 2, 2, false));
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&QuitFunc, &order, 3));
-
- MessageLoop::current()->Run();
-
- // FIFO order.
- ASSERT_EQ(14U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
- EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
-}
-
-void RunTest_RecursiveDenial3(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
- TaskList order;
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&RecursiveSlowFunc, &order, 1, 2, false));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&RecursiveSlowFunc, &order, 2, 2, false));
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- Bind(&OrderedFunc, &order, 3),
- TimeDelta::FromMilliseconds(5));
- MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- Bind(&QuitFunc, &order, 4),
- TimeDelta::FromMilliseconds(5));
-
- MessageLoop::current()->Run();
-
- // FIFO order.
- ASSERT_EQ(16U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(5), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(7), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 4, true));
- EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 4, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(14), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(15), TaskItem(RECURSIVE, 2, false));
-}
-
-void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&RecursiveFunc, &order, 1, 2, true));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&RecursiveFunc, &order, 2, 2, true));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&QuitFunc, &order, 3));
-
- MessageLoop::current()->Run();
-
- // FIFO order.
- ASSERT_EQ(14U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
- EXPECT_EQ(order.Get(6), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(7), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(RECURSIVE, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(RECURSIVE, 1, true));
- EXPECT_EQ(order.Get(11), TaskItem(RECURSIVE, 1, false));
- EXPECT_EQ(order.Get(12), TaskItem(RECURSIVE, 2, true));
- EXPECT_EQ(order.Get(13), TaskItem(RECURSIVE, 2, false));
-}
-
-#if defined(OS_WIN)
// TODO(darin): These tests need to be ported since they test critical
// message loop functionality.
@@ -1007,387 +547,6 @@ void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) {
#endif // defined(OS_WIN)
-void FuncThatPumps(TaskList* order, int cookie) {
- order->RecordStart(PUMPS, cookie);
- {
- MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
- RunLoop().RunUntilIdle();
- }
- order->RecordEnd(PUMPS, cookie);
-}
-
-void FuncThatRuns(TaskList* order, int cookie, RunLoop* run_loop) {
- order->RecordStart(RUNS, cookie);
- {
- MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current());
- run_loop->Run();
- }
- order->RecordEnd(RUNS, cookie);
-}
-
-void FuncThatQuitsNow() {
- MessageLoop::current()->QuitNow();
-}
-
-// Tests that non nestable tasks run in FIFO if there are no nested loops.
-void RunTest_NonNestableWithNoNesting(
- MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- MessageLoop::current()->PostNonNestableTask(
- FROM_HERE,
- Bind(&OrderedFunc, &order, 1));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&QuitFunc, &order, 3));
- MessageLoop::current()->Run();
-
- // FIFO order.
- ASSERT_EQ(6U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(ORDERED, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 1, false));
- EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(3), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(4), TaskItem(QUITMESSAGELOOP, 3, true));
- EXPECT_EQ(order.Get(5), TaskItem(QUITMESSAGELOOP, 3, false));
-}
-
-// Tests that non nestable tasks don't run when there's code in the call stack.
-void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type,
- bool use_delayed) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&FuncThatPumps, &order, 1));
- if (use_delayed) {
- MessageLoop::current()->PostNonNestableDelayedTask(
- FROM_HERE,
- Bind(&OrderedFunc, &order, 2),
- TimeDelta::FromMilliseconds(1));
- } else {
- MessageLoop::current()->PostNonNestableTask(
- FROM_HERE,
- Bind(&OrderedFunc, &order, 2));
- }
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 3));
- MessageLoop::current()->PostTask(
- FROM_HERE,
- Bind(&SleepFunc, &order, 4, TimeDelta::FromMilliseconds(50)));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&OrderedFunc, &order, 5));
- if (use_delayed) {
- MessageLoop::current()->PostNonNestableDelayedTask(
- FROM_HERE,
- Bind(&QuitFunc, &order, 6),
- TimeDelta::FromMilliseconds(2));
- } else {
- MessageLoop::current()->PostNonNestableTask(
- FROM_HERE,
- Bind(&QuitFunc, &order, 6));
- }
-
- MessageLoop::current()->Run();
-
- // FIFO order.
- ASSERT_EQ(12U, order.Size());
- EXPECT_EQ(order.Get(0), TaskItem(PUMPS, 1, true));
- EXPECT_EQ(order.Get(1), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(2), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(3), TaskItem(SLEEP, 4, true));
- EXPECT_EQ(order.Get(4), TaskItem(SLEEP, 4, false));
- EXPECT_EQ(order.Get(5), TaskItem(ORDERED, 5, true));
- EXPECT_EQ(order.Get(6), TaskItem(ORDERED, 5, false));
- EXPECT_EQ(order.Get(7), TaskItem(PUMPS, 1, false));
- EXPECT_EQ(order.Get(8), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(9), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(10), TaskItem(QUITMESSAGELOOP, 6, true));
- EXPECT_EQ(order.Get(11), TaskItem(QUITMESSAGELOOP, 6, false));
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_QuitNow(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop run_loop;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 3));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 4)); // never runs
-
- MessageLoop::current()->Run();
-
- ASSERT_EQ(6U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit works before RunWithID.
-void RunTest_RunLoopQuitOrderBefore(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop run_loop;
-
- run_loop.Quit();
-
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 1)); // never runs
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
-
- run_loop.Run();
-
- ASSERT_EQ(0U, order.Size());
-}
-
-// Tests RunLoopQuit works during RunWithID.
-void RunTest_RunLoopQuitOrderDuring(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop run_loop;
-
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 1));
- MessageLoop::current()->PostTask(
- FROM_HERE, run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2)); // never runs
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow)); // never runs
-
- run_loop.Run();
-
- ASSERT_EQ(2U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit works after RunWithID.
-void RunTest_RunLoopQuitOrderAfter(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop run_loop;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&run_loop)));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 3));
- MessageLoop::current()->PostTask(
- FROM_HERE, run_loop.QuitClosure()); // has no affect
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 4));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&FuncThatQuitsNow));
-
- RunLoop outer_run_loop;
- outer_run_loop.Run();
-
- ASSERT_EQ(8U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 3, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 4, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitTop(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- MessageLoop::current()->PostTask(
- FROM_HERE, outer_run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitNested(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(
- FROM_HERE, outer_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitBogus(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_run_loop;
- RunLoop bogus_run_loop;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&nested_run_loop)));
- MessageLoop::current()->PostTask(
- FROM_HERE, bogus_run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 2));
- MessageLoop::current()->PostTask(
- FROM_HERE, outer_run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_run_loop.QuitClosure());
-
- outer_run_loop.Run();
-
- ASSERT_EQ(4U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
-// Tests RunLoopQuit only quits the corresponding MessageLoop::Run.
-void RunTest_RunLoopQuitDeep(MessageLoop::Type message_loop_type) {
- MessageLoop loop(message_loop_type);
-
- TaskList order;
-
- RunLoop outer_run_loop;
- RunLoop nested_loop1;
- RunLoop nested_loop2;
- RunLoop nested_loop3;
- RunLoop nested_loop4;
-
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 1, Unretained(&nested_loop1)));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 2, Unretained(&nested_loop2)));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 3, Unretained(&nested_loop3)));
- MessageLoop::current()->PostTask(FROM_HERE,
- Bind(&FuncThatRuns, &order, 4, Unretained(&nested_loop4)));
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 5));
- MessageLoop::current()->PostTask(
- FROM_HERE, outer_run_loop.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 6));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_loop1.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 7));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_loop2.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 8));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_loop3.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 9));
- MessageLoop::current()->PostTask(
- FROM_HERE, nested_loop4.QuitClosure());
- MessageLoop::current()->PostTask(
- FROM_HERE, Bind(&OrderedFunc, &order, 10));
-
- outer_run_loop.Run();
-
- ASSERT_EQ(18U, order.Size());
- int task_index = 0;
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 5, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 6, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 7, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 8, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, true));
- EXPECT_EQ(order.Get(task_index++), TaskItem(ORDERED, 9, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 4, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 3, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 2, false));
- EXPECT_EQ(order.Get(task_index++), TaskItem(RUNS, 1, false));
- EXPECT_EQ(static_cast<size_t>(task_index), order.Size());
-}
-
void PostNTasksThenQuit(int posts_remaining) {
if (posts_remaining > 1) {
MessageLoop::current()->PostTask(
@@ -1398,13 +557,6 @@ void PostNTasksThenQuit(int posts_remaining) {
}
}
-void RunTest_RecursivePosts(MessageLoop::Type message_loop_type,
- int num_times) {
- MessageLoop loop(message_loop_type);
- loop.PostTask(FROM_HERE, Bind(&PostNTasksThenQuit, num_times));
- loop.Run();
-}
-
#if defined(OS_WIN)
class DispatcherImpl : public MessageLoopForUI::Dispatcher {
@@ -1620,79 +772,15 @@ void RunTest_WaitForIO() {
// that message loops work properly in all configurations. Of course, in some
// cases, a unit test may only be for a particular type of loop.
-TEST(MessageLoopTest, PostTask) {
- RunTest_PostTask(MessageLoop::TYPE_DEFAULT);
- RunTest_PostTask(MessageLoop::TYPE_UI);
- RunTest_PostTask(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostTask_SEH) {
- RunTest_PostTask_SEH(MessageLoop::TYPE_DEFAULT);
- RunTest_PostTask_SEH(MessageLoop::TYPE_UI);
- RunTest_PostTask_SEH(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_Basic) {
- RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_InDelayOrder) {
- RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_InPostOrder) {
- RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_InPostOrder_2) {
- RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_InPostOrder_3) {
- RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, PostDelayedTask_SharedTimer) {
- RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_DEFAULT);
- RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_UI);
- RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_IO);
-}
+RUN_MESSAGE_LOOP_TESTS(Default, &TypeDefaultMessagePumpFactory);
+RUN_MESSAGE_LOOP_TESTS(UI, &TypeUIMessagePumpFactory);
+RUN_MESSAGE_LOOP_TESTS(IO, &TypeIOMessagePumpFactory);
#if defined(OS_WIN)
TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) {
RunTest_PostDelayedTask_SharedTimer_SubPump();
}
-#endif
-
-// TODO(darin): MessageLoop does not support deleting all tasks in the
-// destructor.
-// Fails, http://crbug.com/50272.
-TEST(MessageLoopTest, DISABLED_EnsureDeletion) {
- RunTest_EnsureDeletion(MessageLoop::TYPE_DEFAULT);
- RunTest_EnsureDeletion(MessageLoop::TYPE_UI);
- RunTest_EnsureDeletion(MessageLoop::TYPE_IO);
-}
-
-// TODO(darin): MessageLoop does not support deleting all tasks in the
-// destructor.
-// Fails, http://crbug.com/50272.
-TEST(MessageLoopTest, DISABLED_EnsureDeletion_Chain) {
- RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_DEFAULT);
- RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_UI);
- RunTest_EnsureDeletion_Chain(MessageLoop::TYPE_IO);
-}
-#if defined(OS_WIN)
TEST(MessageLoopTest, Crasher) {
RunTest_Crasher(MessageLoop::TYPE_DEFAULT);
RunTest_Crasher(MessageLoop::TYPE_UI);
@@ -1704,33 +792,7 @@ TEST(MessageLoopTest, CrasherNasty) {
RunTest_CrasherNasty(MessageLoop::TYPE_UI);
RunTest_CrasherNasty(MessageLoop::TYPE_IO);
}
-#endif // defined(OS_WIN)
-
-TEST(MessageLoopTest, Nesting) {
- RunTest_Nesting(MessageLoop::TYPE_DEFAULT);
- RunTest_Nesting(MessageLoop::TYPE_UI);
- RunTest_Nesting(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RecursiveDenial1) {
- RunTest_RecursiveDenial1(MessageLoop::TYPE_DEFAULT);
- RunTest_RecursiveDenial1(MessageLoop::TYPE_UI);
- RunTest_RecursiveDenial1(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RecursiveDenial3) {
- RunTest_RecursiveDenial3(MessageLoop::TYPE_DEFAULT);
- RunTest_RecursiveDenial3(MessageLoop::TYPE_UI);
- RunTest_RecursiveDenial3(MessageLoop::TYPE_IO);
-}
-TEST(MessageLoopTest, RecursiveSupport1) {
- RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT);
- RunTest_RecursiveSupport1(MessageLoop::TYPE_UI);
- RunTest_RecursiveSupport1(MessageLoop::TYPE_IO);
-}
-
-#if defined(OS_WIN)
// This test occasionally hangs http://crbug.com/44567
TEST(MessageLoopTest, DISABLED_RecursiveDenial2) {
RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT);
@@ -1744,72 +806,6 @@ TEST(MessageLoopTest, RecursiveSupport2) {
}
#endif // defined(OS_WIN)
-TEST(MessageLoopTest, NonNestableWithNoNesting) {
- RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_DEFAULT);
- RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_UI);
- RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, NonNestableInNestedLoop) {
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, false);
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, false);
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, false);
-}
-
-TEST(MessageLoopTest, NonNestableDelayedInNestedLoop) {
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT, true);
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI, true);
- RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO, true);
-}
-
-TEST(MessageLoopTest, QuitNow) {
- RunTest_QuitNow(MessageLoop::TYPE_DEFAULT);
- RunTest_QuitNow(MessageLoop::TYPE_UI);
- RunTest_QuitNow(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitTop) {
- RunTest_RunLoopQuitTop(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitTop(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitTop(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitNested) {
- RunTest_RunLoopQuitNested(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitNested(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitNested(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitBogus) {
- RunTest_RunLoopQuitBogus(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitBogus(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitBogus(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitDeep) {
- RunTest_RunLoopQuitDeep(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitDeep(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitDeep(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitOrderBefore) {
- RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitOrderBefore(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitOrderDuring) {
- RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitOrderDuring(MessageLoop::TYPE_IO);
-}
-
-TEST(MessageLoopTest, RunLoopQuitOrderAfter) {
- RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_DEFAULT);
- RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_UI);
- RunTest_RunLoopQuitOrderAfter(MessageLoop::TYPE_IO);
-}
-
class DummyTaskObserver : public MessageLoop::TaskObserver {
public:
explicit DummyTaskObserver(int num_tasks)
@@ -1948,9 +944,9 @@ TEST(MessageLoopTest, FileDescriptorWatcherOutlivesMessageLoop) {
// and don't run the message loop, just destroy it.
}
}
- if (HANDLE_EINTR(close(pipefds[0])) < 0)
+ if (IGNORE_EINTR(close(pipefds[0])) < 0)
PLOG(ERROR) << "close";
- if (HANDLE_EINTR(close(pipefds[1])) < 0)
+ if (IGNORE_EINTR(close(pipefds[1])) < 0)
PLOG(ERROR) << "close";
}
@@ -1973,9 +969,9 @@ TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) {
controller.StopWatchingFileDescriptor();
}
}
- if (HANDLE_EINTR(close(pipefds[0])) < 0)
+ if (IGNORE_EINTR(close(pipefds[0])) < 0)
PLOG(ERROR) << "close";
- if (HANDLE_EINTR(close(pipefds[1])) < 0)
+ if (IGNORE_EINTR(close(pipefds[1])) < 0)
PLOG(ERROR) << "close";
}
@@ -2086,19 +1082,92 @@ TEST(MessageLoopTest, IsType) {
EXPECT_FALSE(loop.IsType(MessageLoop::TYPE_DEFAULT));
}
-TEST(MessageLoopTest, RecursivePosts) {
- // There was a bug in the MessagePumpGLib where posting tasks recursively
- // caused the message loop to hang, due to the buffer of the internal pipe
- // becoming full. Test all MessageLoop types to ensure this issue does not
- // exist in other MessagePumps.
-
- // On Linux, the pipe buffer size is 64KiB by default. The bug caused one
- // byte accumulated in the pipe per two posts, so we should repeat 128K
- // times to reproduce the bug.
- const int kNumTimes = 1 << 17;
- RunTest_RecursivePosts(MessageLoop::TYPE_DEFAULT, kNumTimes);
- RunTest_RecursivePosts(MessageLoop::TYPE_UI, kNumTimes);
- RunTest_RecursivePosts(MessageLoop::TYPE_IO, kNumTimes);
+#if defined(OS_WIN)
+void EmptyFunction() {}
+
+void PostMultipleTasks() {
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&EmptyFunction));
+}
+
+static const int kSignalMsg = WM_USER + 2;
+
+void PostWindowsMessage(HWND message_hwnd) {
+ PostMessage(message_hwnd, kSignalMsg, 0, 2);
+}
+
+void EndTest(bool* did_run, HWND hwnd) {
+ *did_run = true;
+ PostMessage(hwnd, WM_CLOSE, 0, 0);
+}
+
+int kMyMessageFilterCode = 0x5002;
+
+LRESULT CALLBACK TestWndProcThunk(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ if (message == WM_CLOSE)
+ EXPECT_TRUE(DestroyWindow(hwnd));
+ if (message != kSignalMsg)
+ return DefWindowProc(hwnd, message, wparam, lparam);
+
+ switch (lparam) {
+ case 1:
+ // First, we post a task that will post multiple no-op tasks to make sure
+ // that the pump's incoming task queue does not become empty during the
+ // test.
+ MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&PostMultipleTasks));
+ // Next, we post a task that posts a windows message to trigger the second
+ // stage of the test.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&PostWindowsMessage, hwnd));
+ break;
+ case 2:
+ // Since we're about to enter a modal loop, tell the message loop that we
+ // intend to nest tasks.
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ bool did_run = false;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ base::Bind(&EndTest, &did_run, hwnd));
+ // Run a nested windows-style message loop and verify that our task runs. If
+ // it doesn't, then we'll loop here until the test times out.
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0)) {
+ if (!CallMsgFilter(&msg, kMyMessageFilterCode))
+ DispatchMessage(&msg);
+ // If this message is a WM_CLOSE, explicitly exit the modal loop. Posting
+ // a WM_QUIT should handle this, but unfortunately MessagePumpWin eats
+ // WM_QUIT messages even when running inside a modal loop.
+ if (msg.message == WM_CLOSE)
+ break;
+ }
+ EXPECT_TRUE(did_run);
+ MessageLoop::current()->Quit();
+ break;
+ }
+ return 0;
+}
+
+TEST(MessageLoopTest, AlwaysHaveUserMessageWhenNesting) {
+ MessageLoop loop(MessageLoop::TYPE_UI);
+ HINSTANCE instance = GetModuleFromAddress(&TestWndProcThunk);
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ wc.lpfnWndProc = TestWndProcThunk;
+ wc.hInstance = instance;
+ wc.lpszClassName = L"MessageLoopTest_HWND";
+ ATOM atom = RegisterClassEx(&wc);
+ ASSERT_TRUE(atom);
+
+ HWND message_hwnd = CreateWindow(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0,
+ HWND_MESSAGE, 0, instance, 0);
+ ASSERT_TRUE(message_hwnd) << GetLastError();
+
+ ASSERT_TRUE(PostMessage(message_hwnd, kSignalMsg, 0, 1));
+
+ loop.Run();
+
+ ASSERT_TRUE(UnregisterClass(MAKEINTATOM(atom), instance));
}
+#endif // defined(OS_WIN)
} // namespace base
diff --git a/chromium/base/message_loop/message_pump_android.cc b/chromium/base/message_loop/message_pump_android.cc
index f3f1c9bcb4d..e756fdd3f52 100644
--- a/chromium/base/message_loop/message_pump_android.cc
+++ b/chromium/base/message_loop/message_pump_android.cc
@@ -21,7 +21,7 @@ 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, jint native_delegate) {
+static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate) {
base::MessagePump::Delegate* delegate =
reinterpret_cast<base::MessagePump::Delegate*>(native_delegate);
DCHECK(delegate);
@@ -81,7 +81,8 @@ void MessagePumpForUI::Start(Delegate* delegate) {
DCHECK(env);
system_message_handler_obj_.Reset(
- Java_SystemMessageHandler_create(env, reinterpret_cast<jint>(delegate)));
+ Java_SystemMessageHandler_create(
+ env, reinterpret_cast<intptr_t>(delegate)));
}
void MessagePumpForUI::Quit() {
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 9c7a8fbf651..f3b598c6ba6 100644
--- a/chromium/base/message_loop/message_pump_io_ios_unittest.cc
+++ b/chromium/base/message_loop/message_pump_io_ios_unittest.cc
@@ -31,9 +31,9 @@ class MessagePumpIOSForIOTest : public testing::Test {
}
virtual void TearDown() OVERRIDE {
- if (HANDLE_EINTR(close(pipefds_[0])) < 0)
+ if (IGNORE_EINTR(close(pipefds_[0])) < 0)
PLOG(ERROR) << "close";
- if (HANDLE_EINTR(close(pipefds_[1])) < 0)
+ if (IGNORE_EINTR(close(pipefds_[1])) < 0)
PLOG(ERROR) << "close";
}
diff --git a/chromium/base/message_loop/message_pump_libevent.cc b/chromium/base/message_loop/message_pump_libevent.cc
index 6d862d1d266..26be687c6dd 100644
--- a/chromium/base/message_loop/message_pump_libevent.cc
+++ b/chromium/base/message_loop/message_pump_libevent.cc
@@ -125,11 +125,11 @@ MessagePumpLibevent::~MessagePumpLibevent() {
event_del(wakeup_event_);
delete wakeup_event_;
if (wakeup_pipe_in_ >= 0) {
- if (HANDLE_EINTR(close(wakeup_pipe_in_)) < 0)
+ if (IGNORE_EINTR(close(wakeup_pipe_in_)) < 0)
DPLOG(ERROR) << "close";
}
if (wakeup_pipe_out_ >= 0) {
- if (HANDLE_EINTR(close(wakeup_pipe_out_)) < 0)
+ if (IGNORE_EINTR(close(wakeup_pipe_out_)) < 0)
DPLOG(ERROR) << "close";
}
event_base_free(event_base_);
diff --git a/chromium/base/message_loop/message_pump_libevent_unittest.cc b/chromium/base/message_loop/message_pump_libevent_unittest.cc
index 52ca95bebf7..bf6d21c3280 100644
--- a/chromium/base/message_loop/message_pump_libevent_unittest.cc
+++ b/chromium/base/message_loop/message_pump_libevent_unittest.cc
@@ -30,9 +30,9 @@ class MessagePumpLibeventTest : public testing::Test {
}
virtual void TearDown() OVERRIDE {
- if (HANDLE_EINTR(close(pipefds_[0])) < 0)
+ if (IGNORE_EINTR(close(pipefds_[0])) < 0)
PLOG(ERROR) << "close";
- if (HANDLE_EINTR(close(pipefds_[1])) < 0)
+ if (IGNORE_EINTR(close(pipefds_[1])) < 0)
PLOG(ERROR) << "close";
}
diff --git a/chromium/base/message_loop/message_pump_win.cc b/chromium/base/message_loop/message_pump_win.cc
index 589d0775410..1927473b1eb 100644
--- a/chromium/base/message_loop/message_pump_win.cc
+++ b/chromium/base/message_loop/message_pump_win.cc
@@ -172,30 +172,6 @@ void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
MESSAGE_LOOP_PROBLEM_MAX);
}
-void MessagePumpForUI::PumpOutPendingPaintMessages() {
- // If we are being called outside of the context of Run, then don't try to do
- // any work.
- if (!state_)
- return;
-
- // Create a mini-message-pump to force immediate processing of only Windows
- // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking
- // to get the job done. Actual common max is 4 peeks, but we'll be a little
- // safe here.
- const int kMaxPeekCount = 20;
- int peek_count;
- for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
- MSG msg;
- if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
- break;
- ProcessMessageHelper(msg);
- if (state_->should_quit) // Handle WM_QUIT.
- break;
- }
- // Histogram what was really being used, to help to adjust kMaxPeekCount.
- DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count);
-}
-
//-----------------------------------------------------------------------------
// MessagePumpForUI private:
diff --git a/chromium/base/message_loop/message_pump_win.h b/chromium/base/message_loop/message_pump_win.h
index 2d833540e03..9184058a6c0 100644
--- a/chromium/base/message_loop/message_pump_win.h
+++ b/chromium/base/message_loop/message_pump_win.h
@@ -169,11 +169,6 @@ class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time);
- // Applications can call this to encourage us to process all pending WM_PAINT
- // messages. This method will process all paint messages the Windows Message
- // queue can provide, up to some fixed number (to avoid any infinite loops).
- void PumpOutPendingPaintMessages();
-
private:
static LRESULT CALLBACK WndProcThunk(HWND window_handle,
UINT message,
diff --git a/chromium/base/message_loop/message_pump_x11.cc b/chromium/base/message_loop/message_pump_x11.cc
index dd8b965e695..35dcc040348 100644
--- a/chromium/base/message_loop/message_pump_x11.cc
+++ b/chromium/base/message_loop/message_pump_x11.cc
@@ -53,7 +53,7 @@ GSourceFuncs XSourceFuncs = {
Display* g_xdisplay = NULL;
int g_xinput_opcode = -1;
-bool InitializeXInput2Internal() {
+bool InitializeXInput2() {
Display* display = MessagePumpX11::GetDefaultXDisplay();
if (!display)
return false;
@@ -97,11 +97,6 @@ Window FindEventTarget(const NativeEvent& xev) {
return target;
}
-bool InitializeXInput2() {
- static bool xinput2_supported = InitializeXInput2Internal();
- return xinput2_supported;
-}
-
bool InitializeXkb() {
Display* display = MessagePumpX11::GetDefaultXDisplay();
if (!display)
@@ -153,11 +148,6 @@ Display* MessagePumpX11::GetDefaultXDisplay() {
return g_xdisplay;
}
-// static
-bool MessagePumpX11::HasXInput2() {
- return InitializeXInput2();
-}
-
#if defined(TOOLKIT_GTK)
// static
MessagePumpX11* MessagePumpX11::Current() {
diff --git a/chromium/base/message_loop/message_pump_x11.h b/chromium/base/message_loop/message_pump_x11.h
index f1f678a79c7..015c230a700 100644
--- a/chromium/base/message_loop/message_pump_x11.h
+++ b/chromium/base/message_loop/message_pump_x11.h
@@ -40,9 +40,6 @@ class BASE_EXPORT MessagePumpX11 : public MessagePumpGlib,
// Returns default X Display.
static Display* GetDefaultXDisplay();
- // Returns true if the system supports XINPUT2.
- static bool HasXInput2();
-
// Returns the UI or GPU message pump.
static MessagePumpX11* Current();
diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc
index d500a29cad2..b99f3148f96 100644
--- a/chromium/base/metrics/field_trial.cc
+++ b/chromium/base/metrics/field_trial.cc
@@ -72,25 +72,6 @@ int FieldTrialList::kNoExpirationYear = 0;
//------------------------------------------------------------------------------
// FieldTrial methods and members.
-FieldTrial::FieldTrial(const std::string& trial_name,
- const Probability total_probability,
- const std::string& default_group_name,
- double entropy_value)
- : trial_name_(trial_name),
- divisor_(total_probability),
- default_group_name_(default_group_name),
- random_(GetGroupBoundaryValue(total_probability, entropy_value)),
- accumulated_group_probability_(0),
- next_group_number_(kDefaultGroupNumber + 1),
- group_(kNotFinalized),
- enable_field_trial_(true),
- forced_(false),
- group_reported_(false) {
- DCHECK_GT(total_probability, 0);
- DCHECK(!trial_name_.empty());
- DCHECK(!default_group_name_.empty());
-}
-
FieldTrial::EntropyProvider::~EntropyProvider() {
}
@@ -146,7 +127,8 @@ int FieldTrial::AppendGroup(const std::string& name,
int FieldTrial::group() {
FinalizeGroupChoice();
- FieldTrialList::NotifyFieldTrialGroupSelection(this);
+ if (trial_registered_)
+ FieldTrialList::NotifyFieldTrialGroupSelection(this);
return group_;
}
@@ -157,12 +139,6 @@ const std::string& FieldTrial::group_name() {
return group_name_;
}
-// static
-void FieldTrial::EnableBenchmarking() {
- DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
- enable_benchmarking_ = true;
-}
-
void FieldTrial::SetForced() {
// We might have been forced before (e.g., by CreateFieldTrial) and it's
// first come first served, e.g., command line switch has precedence.
@@ -174,8 +150,50 @@ void FieldTrial::SetForced() {
forced_ = true;
}
+// static
+void FieldTrial::EnableBenchmarking() {
+ DCHECK_EQ(0u, FieldTrialList::GetFieldTrialCount());
+ enable_benchmarking_ = true;
+}
+
+// static
+FieldTrial* FieldTrial::CreateSimulatedFieldTrial(
+ const std::string& trial_name,
+ Probability total_probability,
+ const std::string& default_group_name,
+ double entropy_value) {
+ return new FieldTrial(trial_name, total_probability, default_group_name,
+ entropy_value);
+}
+
+FieldTrial::FieldTrial(const std::string& trial_name,
+ const Probability total_probability,
+ const std::string& default_group_name,
+ double entropy_value)
+ : trial_name_(trial_name),
+ divisor_(total_probability),
+ default_group_name_(default_group_name),
+ random_(GetGroupBoundaryValue(total_probability, entropy_value)),
+ accumulated_group_probability_(0),
+ next_group_number_(kDefaultGroupNumber + 1),
+ group_(kNotFinalized),
+ enable_field_trial_(true),
+ forced_(false),
+ group_reported_(false),
+ trial_registered_(false) {
+ DCHECK_GT(total_probability, 0);
+ DCHECK(!trial_name_.empty());
+ DCHECK(!default_group_name_.empty());
+}
+
FieldTrial::~FieldTrial() {}
+void FieldTrial::SetTrialRegistered() {
+ DCHECK_EQ(kNotFinalized, group_);
+ DCHECK(!trial_registered_);
+ trial_registered_ = true;
+}
+
void FieldTrial::SetGroupChoice(const std::string& group_name, int number) {
group_ = number;
if (group_name.empty())
@@ -434,9 +452,9 @@ FieldTrial* FieldTrialList::CreateFieldTrial(
}
const int kTotalProbability = 100;
field_trial = new FieldTrial(name, kTotalProbability, group_name, 0);
+ FieldTrialList::Register(field_trial);
// Force the trial, which will also finalize the group choice.
field_trial->SetForced();
- FieldTrialList::Register(field_trial);
return field_trial;
}
@@ -510,6 +528,7 @@ void FieldTrialList::Register(FieldTrial* trial) {
AutoLock auto_lock(global_->lock_);
DCHECK(!global_->PreLockedFind(trial->trial_name()));
trial->AddRef();
+ trial->SetTrialRegistered();
global_->registered_[trial->trial_name()] = trial;
}
diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h
index 5060a74cb5b..70ce2f9c45d 100644
--- a/chromium/base/metrics/field_trial.h
+++ b/chromium/base/metrics/field_trial.h
@@ -140,9 +140,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// is used as the group name. This causes a winner to be chosen if none was.
const std::string& group_name();
- // Enable benchmarking sets field trials to a common setting.
- static void EnableBenchmarking();
-
// Set the field trial as forced, meaning that it was setup earlier than
// the hard coded registration of the field trial to override it.
// This allows the code that was hard coded to register the field trial to
@@ -153,6 +150,25 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// be done from the UI thread.
void SetForced();
+ // Enable benchmarking sets field trials to a common setting.
+ static void EnableBenchmarking();
+
+ // Creates a FieldTrial object with the specified parameters, to be used for
+ // simulation of group assignment without actually affecting global field
+ // trial state in the running process. Group assignment will be done based on
+ // |entropy_value|, which must have a range of [0, 1).
+ //
+ // Note: Using this function will not register the field trial globally in the
+ // running process - for that, use FieldTrialList::FactoryGetFieldTrial().
+ //
+ // The ownership of the returned FieldTrial is transfered to the caller which
+ // is responsible for deref'ing it (e.g. by using scoped_refptr<FieldTrial>).
+ static FieldTrial* CreateSimulatedFieldTrial(
+ const std::string& trial_name,
+ Probability total_probability,
+ const std::string& default_group_name,
+ double entropy_value);
+
private:
// Allow tests to access our innards for testing purposes.
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Registration);
@@ -166,9 +182,6 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, ActiveGroupsNotFinalized);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, Save);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, DuplicateRestore);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientId);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, HashClientIdIsUniform);
- FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, NameGroupIds);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOff);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedTurnFeatureOn);
FRIEND_TEST_ALL_PREFIXES(FieldTrialTest, SetForcedChangeDefault_Default);
@@ -187,7 +200,7 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// Creates a field trial with the specified parameters. Group assignment will
// be done based on |entropy_value|, which must have a range of [0, 1).
- FieldTrial(const std::string& name,
+ FieldTrial(const std::string& trial_name,
Probability total_probability,
const std::string& default_group_name,
double entropy_value);
@@ -196,6 +209,10 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// Return the default group name of the FieldTrial.
std::string default_group_name() const { return default_group_name_; }
+ // Marks this trial as having been registered with the FieldTrialList. Must be
+ // called no more than once and before any |group()| calls have occurred.
+ void SetTrialRegistered();
+
// Sets the chosen group name and number.
void SetGroupChoice(const std::string& group_name, int number);
@@ -233,6 +250,7 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// Sum of the probabilities of all appended groups.
Probability accumulated_group_probability_;
+ // The number that will be returned by the next AppendGroup() call.
int next_group_number_;
// The pseudo-randomly assigned group number.
@@ -254,6 +272,10 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> {
// Specifies whether the group choice has been reported to observers.
bool group_reported_;
+ // Whether this trial is registered with the global FieldTrialList and thus
+ // should notify it when its group is queried.
+ bool trial_registered_;
+
// When benchmarking is enabled, field trials all revert to the 'default'
// group.
static bool enable_benchmarking_;
diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc
index 10d3c3f9ed1..a77633e8f09 100644
--- a/chromium/base/metrics/field_trial_unittest.cc
+++ b/chromium/base/metrics/field_trial_unittest.cc
@@ -877,4 +877,47 @@ TEST_F(FieldTrialTest, DoesNotSurpassTotalProbability) {
EXPECT_EQ("2", trial->group_name());
}
+TEST_F(FieldTrialTest, CreateSimulatedFieldTrial) {
+ const char kTrialName[] = "CreateSimulatedFieldTrial";
+ ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName));
+
+ // Different cases to test, e.g. default vs. non default group being chosen.
+ struct {
+ double entropy_value;
+ const char* expected_group;
+ } test_cases[] = {
+ { 0.4, "A" },
+ { 0.85, "B" },
+ { 0.95, kDefaultGroupName },
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) {
+ TestFieldTrialObserver observer;
+ scoped_refptr<FieldTrial> trial(
+ FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName,
+ test_cases[i].entropy_value));
+ trial->AppendGroup("A", 80);
+ trial->AppendGroup("B", 10);
+ EXPECT_EQ(test_cases[i].expected_group, trial->group_name());
+
+ // Field trial shouldn't have been registered with the list.
+ EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName));
+ EXPECT_EQ(0u, FieldTrialList::GetFieldTrialCount());
+
+ // Observer shouldn't have been notified.
+ RunLoop().RunUntilIdle();
+ EXPECT_TRUE(observer.trial_name().empty());
+
+ // The trial shouldn't be in the active set of trials.
+ FieldTrial::ActiveGroups active_groups;
+ FieldTrialList::GetActiveFieldTrialGroups(&active_groups);
+ EXPECT_TRUE(active_groups.empty());
+
+ // The trial shouldn't be listed in the |StatesToString()| result.
+ std::string states;
+ FieldTrialList::StatesToString(&states);
+ EXPECT_TRUE(states.empty());
+ }
+}
+
} // namespace base
diff --git a/chromium/base/metrics/histogram.h b/chromium/base/metrics/histogram.h
index f2566caa3f1..9845362d98a 100644
--- a/chromium/base/metrics/histogram.h
+++ b/chromium/base/metrics/histogram.h
@@ -23,7 +23,7 @@
// agree exactly in type, bucket size and range.
// For Histogram and LinearHistogram, the maximum for a declared range should
-// always be larger (not equal) than minmal range. Zero and
+// always be larger (not equal) than minimal range. Zero and
// HistogramBase::kSampleType_MAX are implicitly added as first and last ranges,
// so the smallest legal bucket_count is 3. However CustomHistogram can have
// bucket count as 2 (when you give a custom ranges vector containing only 1
@@ -96,7 +96,7 @@ class Lock;
// process.
// The following code is generally what a thread-safe static pointer
-// initializaion looks like for a histogram (after a macro is expanded). This
+// initialization looks like for a histogram (after a macro is expanded). This
// sample is an expansion (with comments) of the code for
// HISTOGRAM_CUSTOM_COUNTS().
@@ -107,7 +107,7 @@ class Lock;
static base::subtle::AtomicWord atomic_histogram_pointer = 0;
// Acquire_Load() ensures that we acquire visibility to the pointed-to data
- // in the histogrom.
+ // in the histogram.
base::Histogram* histogram_pointer(reinterpret_cast<base::Histogram*>(
base::subtle::Acquire_Load(&atomic_histogram_pointer)));
@@ -421,7 +421,7 @@ class BASE_EXPORT Histogram : public HistogramBase {
virtual int FindCorruption(const HistogramSamples& samples) const OVERRIDE;
//----------------------------------------------------------------------------
- // Accessors for factory constuction, serialization and testing.
+ // Accessors for factory construction, serialization and testing.
//----------------------------------------------------------------------------
Sample declared_min() const { return declared_min_; }
Sample declared_max() const { return declared_max_; }
@@ -604,7 +604,7 @@ class BASE_EXPORT LinearHistogram : public Histogram {
static HistogramBase* DeserializeInfoImpl(PickleIterator* iter);
// For some ranges, we store a printable description of a bucket range.
- // If there is no desciption, then GetAsciiBucketRange() uses parent class
+ // If there is no description, then GetAsciiBucketRange() uses parent class
// to provide a description.
typedef std::map<Sample, std::string> BucketDescriptionMap;
BucketDescriptionMap bucket_description_;
diff --git a/chromium/base/metrics/histogram_base.cc b/chromium/base/metrics/histogram_base.cc
index 5b46d2990c0..5c6e2d271bb 100644
--- a/chromium/base/metrics/histogram_base.cc
+++ b/chromium/base/metrics/histogram_base.cc
@@ -58,20 +58,6 @@ HistogramBase* DeserializeHistogramInfo(PickleIterator* iter) {
}
}
-void DeserializeHistogramAndAddSamples(PickleIterator* iter) {
- HistogramBase* histogram = DeserializeHistogramInfo(iter);
- if (!histogram)
- return;
-
- if (histogram->flags() & base::HistogramBase::kIPCSerializationSourceFlag) {
- DVLOG(1) << "Single process mode, histogram observed and not copied: "
- << histogram->histogram_name();
- return;
- }
- histogram->AddSamplesFromPickle(iter);
-}
-
-
const HistogramBase::Sample HistogramBase::kSampleType_MAX = INT_MAX;
HistogramBase::HistogramBase(const std::string& name)
diff --git a/chromium/base/metrics/histogram_base.h b/chromium/base/metrics/histogram_base.h
index 46636477f3e..4248bd84a4c 100644
--- a/chromium/base/metrics/histogram_base.h
+++ b/chromium/base/metrics/histogram_base.h
@@ -6,6 +6,7 @@
#define BASE_METRICS_HISTOGRAM_BASE_H_
#include <string>
+#include <vector>
#include "base/atomicops.h"
#include "base/base_export.h"
@@ -43,10 +44,6 @@ std::string HistogramTypeToString(HistogramType type);
BASE_EXPORT_PRIVATE HistogramBase* DeserializeHistogramInfo(
PickleIterator* iter);
-// Create or find existing histogram and add the samples from pickle.
-// Silently returns when seeing any data problem in the pickle.
-BASE_EXPORT void DeserializeHistogramAndAddSamples(PickleIterator* iter);
-
////////////////////////////////////////////////////////////////////////////////
class BASE_EXPORT HistogramBase {
diff --git a/chromium/base/metrics/histogram_base_unittest.cc b/chromium/base/metrics/histogram_base_unittest.cc
index 0e19d56f4c1..4a2963aa6c4 100644
--- a/chromium/base/metrics/histogram_base_unittest.cc
+++ b/chromium/base/metrics/histogram_base_unittest.cc
@@ -61,40 +61,6 @@ TEST_F(HistogramBaseTest, DeserializeHistogram) {
EXPECT_EQ(HistogramBase::kUmaTargetedHistogramFlag, deserialized->flags());
}
-TEST_F(HistogramBaseTest, DeserializeHistogramAndAddSamples) {
- HistogramBase* histogram = Histogram::FactoryGet(
- "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag);
- histogram->Add(1);
- histogram->Add(10);
- histogram->Add(100);
- histogram->Add(1000);
-
- Pickle pickle;
- ASSERT_TRUE(histogram->SerializeInfo(&pickle));
- histogram->SnapshotSamples()->Serialize(&pickle);
-
- PickleIterator iter(pickle);
- DeserializeHistogramAndAddSamples(&iter);
-
- // The histogram has kIPCSerializationSourceFlag. So samples will be ignored.
- scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
- EXPECT_EQ(1, snapshot->GetCount(1));
- EXPECT_EQ(1, snapshot->GetCount(10));
- EXPECT_EQ(1, snapshot->GetCount(100));
- EXPECT_EQ(1, snapshot->GetCount(1000));
-
- // Clear kIPCSerializationSourceFlag to emulate multi-process usage.
- histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag);
- PickleIterator iter2(pickle);
- DeserializeHistogramAndAddSamples(&iter2);
-
- scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
- EXPECT_EQ(2, snapshot2->GetCount(1));
- EXPECT_EQ(2, snapshot2->GetCount(10));
- EXPECT_EQ(2, snapshot2->GetCount(100));
- EXPECT_EQ(2, snapshot2->GetCount(1000));
-}
-
TEST_F(HistogramBaseTest, DeserializeLinearHistogram) {
HistogramBase* histogram = LinearHistogram::FactoryGet(
"TestHistogram", 1, 1000, 10,
diff --git a/chromium/base/metrics/histogram_delta_serialization.cc b/chromium/base/metrics/histogram_delta_serialization.cc
new file mode 100644
index 00000000000..924916db76e
--- /dev/null
+++ b/chromium/base/metrics/histogram_delta_serialization.cc
@@ -0,0 +1,111 @@
+// 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/metrics/histogram_delta_serialization.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+#include "base/pickle.h"
+#include "base/safe_numerics.h"
+#include "base/values.h"
+
+namespace base {
+
+namespace {
+
+// Create or find existing histogram and add the samples from pickle.
+// Silently returns when seeing any data problem in the pickle.
+void DeserializeHistogramAndAddSamples(PickleIterator* iter) {
+ HistogramBase* histogram = DeserializeHistogramInfo(iter);
+ if (!histogram)
+ return;
+
+ if (histogram->flags() & HistogramBase::kIPCSerializationSourceFlag) {
+ DVLOG(1) << "Single process mode, histogram observed and not copied: "
+ << histogram->histogram_name();
+ return;
+ }
+ histogram->AddSamplesFromPickle(iter);
+}
+
+} // namespace
+
+HistogramDeltaSerialization::HistogramDeltaSerialization(
+ const std::string& caller_name)
+ : histogram_snapshot_manager_(this),
+ serialized_deltas_(NULL) {
+ inconsistencies_histogram_ =
+ LinearHistogram::FactoryGet(
+ "Histogram.Inconsistencies" + caller_name, 1,
+ HistogramBase::NEVER_EXCEEDED_VALUE,
+ HistogramBase::NEVER_EXCEEDED_VALUE + 1,
+ HistogramBase::kUmaTargetedHistogramFlag);
+
+ inconsistencies_unique_histogram_ =
+ LinearHistogram::FactoryGet(
+ "Histogram.Inconsistencies" + caller_name + "Unique", 1,
+ HistogramBase::NEVER_EXCEEDED_VALUE,
+ HistogramBase::NEVER_EXCEEDED_VALUE + 1,
+ HistogramBase::kUmaTargetedHistogramFlag);
+
+ inconsistent_snapshot_histogram_ =
+ Histogram::FactoryGet(
+ "Histogram.InconsistentSnapshot" + caller_name, 1, 1000000, 50,
+ HistogramBase::kUmaTargetedHistogramFlag);
+}
+
+HistogramDeltaSerialization::~HistogramDeltaSerialization() {
+}
+
+void HistogramDeltaSerialization::PrepareAndSerializeDeltas(
+ std::vector<std::string>* serialized_deltas) {
+ serialized_deltas_ = serialized_deltas;
+ // Note: Before serializing, we set the kIPCSerializationSourceFlag for all
+ // the histograms, so that the receiving process can distinguish them from the
+ // local histograms.
+ histogram_snapshot_manager_.PrepareDeltas(
+ Histogram::kIPCSerializationSourceFlag, false);
+ serialized_deltas_ = NULL;
+}
+
+// static
+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()));
+ PickleIterator iter(pickle);
+ DeserializeHistogramAndAddSamples(&iter);
+ }
+}
+
+void HistogramDeltaSerialization::RecordDelta(
+ const HistogramBase& histogram,
+ const HistogramSamples& snapshot) {
+ DCHECK_NE(0, snapshot.TotalCount());
+
+ Pickle pickle;
+ histogram.SerializeInfo(&pickle);
+ snapshot.Serialize(&pickle);
+ serialized_deltas_->push_back(
+ std::string(static_cast<const char*>(pickle.data()), pickle.size()));
+}
+
+void HistogramDeltaSerialization::InconsistencyDetected(
+ HistogramBase::Inconsistency problem) {
+ inconsistencies_histogram_->Add(problem);
+}
+
+void HistogramDeltaSerialization::UniqueInconsistencyDetected(
+ HistogramBase::Inconsistency problem) {
+ inconsistencies_unique_histogram_->Add(problem);
+}
+
+void HistogramDeltaSerialization::InconsistencyDetectedInLoggedCount(
+ int amount) {
+ inconsistent_snapshot_histogram_->Add(std::abs(amount));
+}
+
+} // namespace base
diff --git a/chromium/base/metrics/histogram_delta_serialization.h b/chromium/base/metrics/histogram_delta_serialization.h
new file mode 100644
index 00000000000..ccadb12894f
--- /dev/null
+++ b/chromium/base/metrics/histogram_delta_serialization.h
@@ -0,0 +1,65 @@
+// 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_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
+#define BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
+
+#include <string>
+#include <vector>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/histogram_flattener.h"
+#include "base/metrics/histogram_snapshot_manager.h"
+
+namespace base {
+
+class HistogramBase;
+
+// Serializes and restores histograms deltas.
+class BASE_EXPORT HistogramDeltaSerialization : public HistogramFlattener {
+ public:
+ // |caller_name| is string used in histograms for counting inconsistencies.
+ explicit HistogramDeltaSerialization(const std::string& caller_name);
+ virtual ~HistogramDeltaSerialization();
+
+ // Computes deltas in histogram bucket counts relative to the previous call to
+ // this method. Stores the deltas in serialized form into |serialized_deltas|.
+ // If |serialized_deltas| is NULL, no data is serialized, though the next call
+ // will compute the deltas relative to this one.
+ void PrepareAndSerializeDeltas(std::vector<std::string>* serialized_deltas);
+
+ // Deserialize deltas and add samples to corresponding histograms, creating
+ // them if necessary. Silently ignores errors in |serialized_deltas|.
+ static void DeserializeAndAddSamples(
+ const std::vector<std::string>& serialized_deltas);
+
+ private:
+ // HistogramFlattener implementation.
+ virtual void RecordDelta(const HistogramBase& histogram,
+ const HistogramSamples& snapshot) OVERRIDE;
+ virtual void InconsistencyDetected(
+ HistogramBase::Inconsistency problem) OVERRIDE;
+ virtual void UniqueInconsistencyDetected(
+ HistogramBase::Inconsistency problem) OVERRIDE;
+ virtual void InconsistencyDetectedInLoggedCount(int amount) OVERRIDE;
+
+ // Calculates deltas in histogram counters.
+ HistogramSnapshotManager histogram_snapshot_manager_;
+
+ // Output buffer for serialized deltas.
+ std::vector<std::string>* serialized_deltas_;
+
+ // Histograms to count inconsistencies in snapshots.
+ HistogramBase* inconsistencies_histogram_;
+ HistogramBase* inconsistencies_unique_histogram_;
+ HistogramBase* inconsistent_snapshot_histogram_;
+
+ DISALLOW_COPY_AND_ASSIGN(HistogramDeltaSerialization);
+};
+
+} // namespace base
+
+#endif // BASE_METRICS_HISTOGRAM_DELTA_SERIALIZATION_H_
diff --git a/chromium/base/metrics/histogram_delta_serialization_unittest.cc b/chromium/base/metrics/histogram_delta_serialization_unittest.cc
new file mode 100644
index 00000000000..b53520c5098
--- /dev/null
+++ b/chromium/base/metrics/histogram_delta_serialization_unittest.cc
@@ -0,0 +1,54 @@
+// 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/metrics/histogram_delta_serialization.h"
+
+#include <vector>
+
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+#include "base/metrics/statistics_recorder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(HistogramDeltaSerializationTest, DeserializeHistogramAndAddSamples) {
+ StatisticsRecorder statistic_recorder;
+ HistogramDeltaSerialization serializer("HistogramDeltaSerializationTest");
+ std::vector<std::string> deltas;
+ // Nothing was changed yet.
+ serializer.PrepareAndSerializeDeltas(&deltas);
+ EXPECT_TRUE(deltas.empty());
+
+ HistogramBase* histogram = Histogram::FactoryGet(
+ "TestHistogram", 1, 1000, 10, HistogramBase::kIPCSerializationSourceFlag);
+ histogram->Add(1);
+ histogram->Add(10);
+ histogram->Add(100);
+ histogram->Add(1000);
+
+ serializer.PrepareAndSerializeDeltas(&deltas);
+ EXPECT_FALSE(deltas.empty());
+
+ HistogramDeltaSerialization::DeserializeAndAddSamples(deltas);
+
+ // The histogram has kIPCSerializationSourceFlag. So samples will be ignored.
+ scoped_ptr<HistogramSamples> snapshot(histogram->SnapshotSamples());
+ EXPECT_EQ(1, snapshot->GetCount(1));
+ EXPECT_EQ(1, snapshot->GetCount(10));
+ EXPECT_EQ(1, snapshot->GetCount(100));
+ EXPECT_EQ(1, snapshot->GetCount(1000));
+
+ // Clear kIPCSerializationSourceFlag to emulate multi-process usage.
+ histogram->ClearFlags(HistogramBase::kIPCSerializationSourceFlag);
+ HistogramDeltaSerialization::DeserializeAndAddSamples(deltas);
+
+ scoped_ptr<HistogramSamples> snapshot2(histogram->SnapshotSamples());
+ EXPECT_EQ(2, snapshot2->GetCount(1));
+ EXPECT_EQ(2, snapshot2->GetCount(10));
+ EXPECT_EQ(2, snapshot2->GetCount(100));
+ EXPECT_EQ(2, snapshot2->GetCount(1000));
+}
+
+} // namespace base
diff --git a/chromium/base/metrics/histogram_snapshot_manager.cc b/chromium/base/metrics/histogram_snapshot_manager.cc
index 2301819ad30..cb594bd96d9 100644
--- a/chromium/base/metrics/histogram_snapshot_manager.cc
+++ b/chromium/base/metrics/histogram_snapshot_manager.cc
@@ -94,7 +94,7 @@ void HistogramSnapshotManager::PrepareDelta(const HistogramBase& histogram) {
to_log = snapshot.get();
}
- if (to_log->redundant_count() > 0)
+ if (to_log->TotalCount() > 0)
histogram_flattener_->RecordDelta(histogram, *to_log);
}
diff --git a/chromium/base/metrics/statistics_recorder.cc b/chromium/base/metrics/statistics_recorder.cc
index f23c81054cf..62617c5d804 100644
--- a/chromium/base/metrics/statistics_recorder.cc
+++ b/chromium/base/metrics/statistics_recorder.cc
@@ -6,11 +6,13 @@
#include "base/at_exit.h"
#include "base/debug/leak_annotations.h"
+#include "base/json/string_escape.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
+#include "base/values.h"
using std::list;
using std::string;
@@ -163,6 +165,36 @@ void StatisticsRecorder::WriteGraph(const std::string& query,
}
// static
+std::string StatisticsRecorder::ToJSON(const std::string& query) {
+ if (!IsActive())
+ return std::string();
+
+ std::string output("{");
+ if (!query.empty()) {
+ output += "\"query\":";
+ EscapeJSONString(query, true, &output);
+ output += ",";
+ }
+
+ Histograms snapshot;
+ GetSnapshot(query, &snapshot);
+ output += "\"histograms\":[";
+ bool first_histogram = true;
+ for (Histograms::const_iterator it = snapshot.begin(); it != snapshot.end();
+ ++it) {
+ if (first_histogram)
+ first_histogram = false;
+ else
+ output += ",";
+ std::string json;
+ (*it)->WriteJSON(&json);
+ output += json;
+ }
+ output += "]}";
+ return output;
+}
+
+// static
void StatisticsRecorder::GetHistograms(Histograms* output) {
if (lock_ == NULL)
return;
diff --git a/chromium/base/metrics/statistics_recorder.h b/chromium/base/metrics/statistics_recorder.h
index 9a552253243..0716e80572d 100644
--- a/chromium/base/metrics/statistics_recorder.h
+++ b/chromium/base/metrics/statistics_recorder.h
@@ -49,12 +49,16 @@ class BASE_EXPORT StatisticsRecorder {
static const BucketRanges* RegisterOrDeleteDuplicateRanges(
const BucketRanges* ranges);
- // Methods for printing histograms. Only histograms which have query as
- // a substring are written to output (an empty string will process all
- // registered histograms).
+ // Methods for appending histogram data to a string. Only histograms which
+ // have |query| as a substring are written to |output| (an empty string will
+ // process all registered histograms).
static void WriteHTMLGraph(const std::string& query, std::string* output);
static void WriteGraph(const std::string& query, std::string* output);
+ // Returns the histograms with |query| as a substring as JSON text (an empty
+ // |query| will process all registered histograms).
+ static std::string ToJSON(const std::string& query);
+
// Method for extracting histograms which were marked for use by UMA.
static void GetHistograms(Histograms* output);
@@ -85,6 +89,8 @@ class BASE_EXPORT StatisticsRecorder {
friend class HistogramTest;
friend class SparseHistogramTest;
friend class StatisticsRecorderTest;
+ FRIEND_TEST_ALL_PREFIXES(HistogramDeltaSerializationTest,
+ DeserializeHistogramAndAddSamples);
// The constructor just initializes static members. Usually client code should
// use Initialize to do this. But in test code, you can friend this class and
diff --git a/chromium/base/metrics/statistics_recorder_unittest.cc b/chromium/base/metrics/statistics_recorder_unittest.cc
index 22504ddfdda..906e6422549 100644
--- a/chromium/base/metrics/statistics_recorder_unittest.cc
+++ b/chromium/base/metrics/statistics_recorder_unittest.cc
@@ -4,9 +4,11 @@
#include <vector>
+#include "base/json/json_reader.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
#include "base/metrics/statistics_recorder.h"
+#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
@@ -263,4 +265,64 @@ TEST_F(StatisticsRecorderTest, BucketRangesSharing) {
EXPECT_EQ(2u, ranges.size());
}
+TEST_F(StatisticsRecorderTest, ToJSON) {
+ HISTOGRAM_COUNTS("TestHistogram1", 30);
+ HISTOGRAM_COUNTS("TestHistogram1", 40);
+ HISTOGRAM_COUNTS("TestHistogram2", 30);
+ HISTOGRAM_COUNTS("TestHistogram2", 40);
+
+ std::string json(StatisticsRecorder::ToJSON(std::string()));
+
+ // Check for valid JSON.
+ scoped_ptr<Value> root;
+ root.reset(JSONReader::Read(json));
+ ASSERT_TRUE(root.get());
+
+ DictionaryValue* root_dict = NULL;
+ ASSERT_TRUE(root->GetAsDictionary(&root_dict));
+
+ // No query should be set.
+ ASSERT_FALSE(root_dict->HasKey("query"));
+
+ ListValue* histogram_list = NULL;
+ ASSERT_TRUE(root_dict->GetList("histograms", &histogram_list));
+ ASSERT_EQ(2u, histogram_list->GetSize());
+
+ // Examine the first histogram.
+ DictionaryValue* histogram_dict = NULL;
+ ASSERT_TRUE(histogram_list->GetDictionary(0, &histogram_dict));
+
+ int sample_count;
+ ASSERT_TRUE(histogram_dict->GetInteger("count", &sample_count));
+ EXPECT_EQ(2, sample_count);
+
+ // Test the query filter.
+ std::string query("TestHistogram2");
+ json = StatisticsRecorder::ToJSON(query);
+
+ root.reset(JSONReader::Read(json));
+ ASSERT_TRUE(root.get());
+ ASSERT_TRUE(root->GetAsDictionary(&root_dict));
+
+ std::string query_value;
+ ASSERT_TRUE(root_dict->GetString("query", &query_value));
+ EXPECT_EQ(query, query_value);
+
+ ASSERT_TRUE(root_dict->GetList("histograms", &histogram_list));
+ ASSERT_EQ(1u, histogram_list->GetSize());
+
+ ASSERT_TRUE(histogram_list->GetDictionary(0, &histogram_dict));
+
+ std::string histogram_name;
+ ASSERT_TRUE(histogram_dict->GetString("name", &histogram_name));
+ EXPECT_EQ("TestHistogram2", histogram_name);
+
+ json.clear();
+ UninitializeStatisticsRecorder();
+
+ // No data should be returned.
+ json = StatisticsRecorder::ToJSON(query);
+ EXPECT_TRUE(json.empty());
+}
+
} // namespace base
diff --git a/chromium/base/metrics/stats_table.cc b/chromium/base/metrics/stats_table.cc
index 403e28c9297..716b7cfc91d 100644
--- a/chromium/base/metrics/stats_table.cc
+++ b/chromium/base/metrics/stats_table.cc
@@ -15,7 +15,9 @@
#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 {
@@ -88,10 +90,10 @@ inline int AlignedSize(int size) {
} // namespace
-// The StatsTable::Private maintains convenience pointers into the
+// The StatsTable::Internal maintains convenience pointers into the
// shared memory segment. Use this class to keep the data structure
// clean and accessible.
-class StatsTable::Private {
+class StatsTable::Internal {
public:
// Various header information contained in the memory mapped segment.
struct TableHeader {
@@ -101,12 +103,14 @@ class StatsTable::Private {
int max_threads;
};
- // Construct a new Private based on expected size parameters, or
+ // Construct a new Internal based on expected size parameters, or
// return NULL on failure.
- static Private* New(const std::string& name, int size,
- int max_threads, int max_counters);
+ static Internal* New(const std::string& name,
+ int size,
+ int max_threads,
+ int max_counters);
- SharedMemory* shared_memory() { return &shared_memory_; }
+ SharedMemory* shared_memory() { return shared_memory_.get(); }
// Accessors for our header pointers
TableHeader* table_header() const { return table_header_; }
@@ -136,8 +140,9 @@ class StatsTable::Private {
private:
// Constructor is private because you should use New() instead.
- Private()
- : table_header_(NULL),
+ explicit Internal(SharedMemory* shared_memory)
+ : shared_memory_(shared_memory),
+ table_header_(NULL),
thread_names_table_(NULL),
thread_tid_table_(NULL),
thread_pid_table_(NULL),
@@ -145,6 +150,10 @@ class StatsTable::Private {
data_table_(NULL) {
}
+ // Create or open the SharedMemory used by the stats table.
+ static SharedMemory* CreateSharedMemory(const std::string& name,
+ int size);
+
// Initializes the table on first access. Sets header values
// appropriately and zeroes all counters.
void InitializeTable(void* memory, int size, int max_counters,
@@ -153,41 +162,68 @@ class StatsTable::Private {
// Initializes our in-memory pointers into a pre-created StatsTable.
void ComputeMappedPointers(void* memory);
- SharedMemory shared_memory_;
+ scoped_ptr<SharedMemory> shared_memory_;
TableHeader* table_header_;
char* thread_names_table_;
PlatformThreadId* thread_tid_table_;
int* thread_pid_table_;
char* counter_names_table_;
int* data_table_;
+
+ DISALLOW_COPY_AND_ASSIGN(Internal);
};
// static
-StatsTable::Private* StatsTable::Private::New(const std::string& name,
- int size,
- int max_threads,
- int max_counters) {
- scoped_ptr<Private> priv(new Private());
- if (!priv->shared_memory_.CreateNamed(name, true, size))
+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));
+ if (!shared_memory.get())
return NULL;
- if (!priv->shared_memory_.Map(size))
+ if (!shared_memory->Map(size))
return NULL;
- void* memory = priv->shared_memory_.memory();
+ void* memory = shared_memory->memory();
+ scoped_ptr<Internal> internal(new Internal(shared_memory.release()));
TableHeader* header = static_cast<TableHeader*>(memory);
// If the version does not match, then assume the table needs
// to be initialized.
if (header->version != kTableVersion)
- priv->InitializeTable(memory, size, max_counters, max_threads);
+ internal->InitializeTable(memory, size, max_counters, max_threads);
// We have a valid table, so compute our pointers.
- priv->ComputeMappedPointers(memory);
+ internal->ComputeMappedPointers(memory);
+
+ return internal.release();
+}
- return priv.release();
+// static
+SharedMemory* StatsTable::Internal::CreateSharedMemory(const std::string& name,
+ 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);
+ }
+ // Otherwise we need to create it.
+ scoped_ptr<SharedMemory> shared_memory(new SharedMemory());
+ if (!shared_memory->CreateAnonymous(size))
+ return NULL;
+ return shared_memory.release();
+#elif defined(OS_WIN)
+ scoped_ptr<SharedMemory> shared_memory(new SharedMemory());
+ if (!shared_memory->CreateNamed(name, true, size))
+ return NULL;
+ return shared_memory.release();
+#endif
}
-void StatsTable::Private::InitializeTable(void* memory, int size,
+void StatsTable::Internal::InitializeTable(void* memory, int size,
int max_counters,
int max_threads) {
// Zero everything.
@@ -201,7 +237,7 @@ void StatsTable::Private::InitializeTable(void* memory, int size,
header->max_threads = max_threads;
}
-void StatsTable::Private::ComputeMappedPointers(void* memory) {
+void StatsTable::Internal::ComputeMappedPointers(void* memory) {
char* data = static_cast<char*>(memory);
int offset = 0;
@@ -252,19 +288,19 @@ StatsTable* global_table = NULL;
StatsTable::StatsTable(const std::string& name, int max_threads,
int max_counters)
- : impl_(NULL),
+ : internal_(NULL),
tls_index_(SlotReturnFunction) {
int table_size =
- AlignedSize(sizeof(Private::TableHeader)) +
+ AlignedSize(sizeof(Internal::TableHeader)) +
AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) +
AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) +
AlignedSize(max_threads * sizeof(int)) +
AlignedSize(max_threads * sizeof(int)) +
AlignedSize((sizeof(int) * (max_counters * max_threads)));
- impl_ = Private::New(name, table_size, max_threads, max_counters);
+ internal_ = Internal::New(name, table_size, max_threads, max_counters);
- if (!impl_)
+ if (!internal_)
DPLOG(ERROR) << "StatsTable did not initialize";
}
@@ -278,7 +314,7 @@ StatsTable::~StatsTable() {
tls_index_.Free();
// Cleanup our shared memory.
- delete impl_;
+ delete internal_;
// If we are the global table, unregister ourselves.
if (global_table == this)
@@ -302,14 +338,14 @@ int StatsTable::GetSlot() const {
int StatsTable::RegisterThread(const std::string& name) {
int slot = 0;
- if (!impl_)
+ if (!internal_)
return 0;
// Registering a thread requires that we lock the shared memory
// so that two threads don't grab the same slot. Fortunately,
// thread creation shouldn't happen in inner loops.
{
- SharedMemoryAutoLock lock(impl_->shared_memory());
+ SharedMemoryAutoLock lock(internal_->shared_memory());
slot = FindEmptyThread();
if (!slot) {
return 0;
@@ -319,10 +355,10 @@ int StatsTable::RegisterThread(const std::string& name) {
std::string thread_name = name;
if (name.empty())
thread_name = kUnknownName;
- strlcpy(impl_->thread_name(slot), thread_name.c_str(),
+ strlcpy(internal_->thread_name(slot), thread_name.c_str(),
kMaxThreadNameLength);
- *(impl_->thread_tid(slot)) = PlatformThread::CurrentId();
- *(impl_->thread_pid(slot)) = GetCurrentProcId();
+ *(internal_->thread_tid(slot)) = PlatformThread::CurrentId();
+ *(internal_->thread_pid(slot)) = GetCurrentProcId();
}
// Set our thread local storage.
@@ -334,14 +370,14 @@ int StatsTable::RegisterThread(const std::string& name) {
}
int StatsTable::CountThreadsRegistered() const {
- if (!impl_)
+ if (!internal_)
return 0;
// Loop through the shared memory and count the threads that are active.
// We intentionally do not lock the table during the operation.
int count = 0;
- for (int index = 1; index <= impl_->max_threads(); index++) {
- char* name = impl_->thread_name(index);
+ for (int index = 1; index <= internal_->max_threads(); index++) {
+ char* name = internal_->thread_name(index);
if (*name != '\0')
count++;
}
@@ -352,7 +388,7 @@ int StatsTable::FindCounter(const std::string& name) {
// Note: the API returns counters numbered from 1..N, although
// internally, the array is 0..N-1. This is so that we can return
// zero as "not found".
- if (!impl_)
+ if (!internal_)
return 0;
// Create a scope for our auto-lock.
@@ -371,20 +407,20 @@ int StatsTable::FindCounter(const std::string& name) {
}
int* StatsTable::GetLocation(int counter_id, int slot_id) const {
- if (!impl_)
+ if (!internal_)
return NULL;
- if (slot_id > impl_->max_threads())
+ if (slot_id > internal_->max_threads())
return NULL;
- int* row = impl_->row(counter_id);
+ int* row = internal_->row(counter_id);
return &(row[slot_id-1]);
}
const char* StatsTable::GetRowName(int index) const {
- if (!impl_)
+ if (!internal_)
return NULL;
- return impl_->counter_name(index);
+ return internal_->counter_name(index);
}
int StatsTable::GetRowValue(int index) const {
@@ -392,13 +428,13 @@ int StatsTable::GetRowValue(int index) const {
}
int StatsTable::GetRowValue(int index, int pid) const {
- if (!impl_)
+ if (!internal_)
return 0;
int rv = 0;
- int* row = impl_->row(index);
- for (int slot_id = 1; slot_id <= impl_->max_threads(); slot_id++) {
- if (pid == 0 || *impl_->thread_pid(slot_id) == pid)
+ int* row = internal_->row(index);
+ for (int slot_id = 1; slot_id <= internal_->max_threads(); slot_id++) {
+ if (pid == 0 || *internal_->thread_pid(slot_id) == pid)
rv += row[slot_id-1];
}
return rv;
@@ -409,7 +445,7 @@ int StatsTable::GetCounterValue(const std::string& name) {
}
int StatsTable::GetCounterValue(const std::string& name, int pid) {
- if (!impl_)
+ if (!internal_)
return 0;
int row = FindCounter(name);
@@ -419,15 +455,15 @@ int StatsTable::GetCounterValue(const std::string& name, int pid) {
}
int StatsTable::GetMaxCounters() const {
- if (!impl_)
+ if (!internal_)
return 0;
- return impl_->max_counters();
+ return internal_->max_counters();
}
int StatsTable::GetMaxThreads() const {
- if (!impl_)
+ if (!internal_)
return 0;
- return impl_->max_threads();
+ return internal_->max_threads();
}
int* StatsTable::FindLocation(const char* name) {
@@ -457,10 +493,10 @@ void StatsTable::UnregisterThread() {
void StatsTable::UnregisterThread(TLSData* data) {
if (!data)
return;
- DCHECK(impl_);
+ DCHECK(internal_);
// Mark the slot free by zeroing out the thread name.
- char* name = impl_->thread_name(data->slot);
+ char* name = internal_->thread_name(data->slot);
*name = '\0';
// Remove the calling thread's TLS so that it cannot use the slot.
@@ -488,16 +524,16 @@ int StatsTable::FindEmptyThread() const {
// in TLS, which is always initialized to zero, not -1. If 0 were
// returned as a valid slot number, it would be confused with the
// uninitialized state.
- if (!impl_)
+ if (!internal_)
return 0;
int index = 1;
- for (; index <= impl_->max_threads(); index++) {
- char* name = impl_->thread_name(index);
+ for (; index <= internal_->max_threads(); index++) {
+ char* name = internal_->thread_name(index);
if (!*name)
break;
}
- if (index > impl_->max_threads())
+ if (index > internal_->max_threads())
return 0; // The table is full.
return index;
}
@@ -510,12 +546,12 @@ int StatsTable::FindCounterOrEmptyRow(const std::string& name) const {
// There isn't much reason for this other than to be consistent
// with the way we track columns for thread slots. (See comments
// in FindEmptyThread for why it is done this way).
- if (!impl_)
+ if (!internal_)
return 0;
int free_slot = 0;
- for (int index = 1; index <= impl_->max_counters(); index++) {
- char* row_name = impl_->counter_name(index);
+ for (int index = 1; index <= internal_->max_counters(); index++) {
+ char* row_name = internal_->counter_name(index);
if (!*row_name && !free_slot)
free_slot = index; // save that we found a free slot
else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength))
@@ -525,14 +561,14 @@ int StatsTable::FindCounterOrEmptyRow(const std::string& name) const {
}
int StatsTable::AddCounter(const std::string& name) {
- if (!impl_)
+ if (!internal_)
return 0;
int counter_id = 0;
{
// To add a counter to the shared memory, we need the
// shared memory lock.
- SharedMemoryAutoLock lock(impl_->shared_memory());
+ SharedMemoryAutoLock lock(internal_->shared_memory());
// We have space, so create a new counter.
counter_id = FindCounterOrEmptyRow(name);
@@ -542,7 +578,7 @@ int StatsTable::AddCounter(const std::string& name) {
std::string counter_name = name;
if (name.empty())
counter_name = kUnknownName;
- strlcpy(impl_->counter_name(counter_id), counter_name.c_str(),
+ strlcpy(internal_->counter_name(counter_id), counter_name.c_str(),
kMaxCounterNameLength);
}
@@ -565,4 +601,12 @@ StatsTable::TLSData* StatsTable::GetTLSData() const {
return data;
}
+#if defined(OS_POSIX)
+SharedMemoryHandle StatsTable::GetSharedMemoryHandle() const {
+ if (!internal_)
+ return SharedMemory::NULLHandle();
+ return internal_->shared_memory()->handle();
+}
+#endif
+
} // namespace base
diff --git a/chromium/base/metrics/stats_table.h b/chromium/base/metrics/stats_table.h
index 153af38c928..49ba79f88ba 100644
--- a/chromium/base/metrics/stats_table.h
+++ b/chromium/base/metrics/stats_table.h
@@ -25,6 +25,7 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
+#include "base/memory/shared_memory.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_local_storage.h"
@@ -116,6 +117,11 @@ class BASE_EXPORT StatsTable {
// The maxinum number of threads/columns in the table.
int GetMaxThreads() const;
+#if defined(OS_POSIX)
+ // Get the underlying shared memory handle for the table.
+ base::SharedMemoryHandle GetSharedMemoryHandle() const;
+#endif
+
// The maximum length (in characters) of a Thread's name including
// null terminator, as stored in the shared memory.
static const int kMaxThreadNameLength = 32;
@@ -130,7 +136,7 @@ class BASE_EXPORT StatsTable {
static int* FindLocation(const char *name);
private:
- class Private;
+ class Internal;
struct TLSData;
typedef hash_map<std::string, int> CountersMap;
@@ -172,7 +178,7 @@ class BASE_EXPORT StatsTable {
// initialized.
TLSData* GetTLSData() const;
- Private* impl_;
+ Internal* internal_;
// The counters_lock_ protects the counters_ hash table.
base::Lock counters_lock_;
diff --git a/chromium/base/move.h b/chromium/base/move.h
index d2cd3df4f7d..1c67155be1c 100644
--- a/chromium/base/move.h
+++ b/chromium/base/move.h
@@ -136,6 +136,16 @@
// choose the one that adheres to the standard.
//
//
+// WHY HAVE typedef void MoveOnlyTypeForCPP03
+//
+// Callback<>/Bind() needs to understand movable-but-not-copyable semantics
+// to call .Pass() appropriately when it is expected to transfer the value.
+// The cryptic typedef MoveOnlyTypeForCPP03 is added to make this check
+// easy and automatic in helper templates for Callback<>/Bind().
+// See IsMoveOnlyType template and its usage in base/callback_internal.h
+// for more details.
+//
+//
// COMPARED TO C++11
//
// In C++11, you would implement this functionality using an r-value reference
@@ -202,6 +212,7 @@
public: \
operator rvalue_type() { return rvalue_type(this); } \
type Pass() { return type(rvalue_type(this)); } \
+ typedef void MoveOnlyTypeForCPP03; \
private:
#endif // BASE_MOVE_H_
diff --git a/chromium/base/nix/mime_util_xdg.cc b/chromium/base/nix/mime_util_xdg.cc
index 1a41394fc15..dd2c7eb663c 100644
--- a/chromium/base/nix/mime_util_xdg.cc
+++ b/chromium/base/nix/mime_util_xdg.cc
@@ -32,13 +32,12 @@ class IconTheme;
// None of the XDG stuff is thread-safe, so serialize all access under
// this lock.
-base::LazyInstance<base::Lock>::Leaky
- g_mime_util_xdg_lock = LAZY_INSTANCE_INITIALIZER;
+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, base::Time> IconDirMtimeMap;
+ typedef std::map<FilePath, Time> IconDirMtimeMap;
typedef std::vector<std::string> IconFormats;
// Specified by XDG icon theme specs.
@@ -62,7 +61,7 @@ class MimeUtilConstants {
// The default theme.
IconTheme* default_themes_[kDefaultThemeNum];
- base::TimeTicks last_check_time_;
+ TimeTicks last_check_time_;
// The current icon theme, usually set through GTK theme integration.
std::string icon_theme_name_;
@@ -159,7 +158,7 @@ class IconTheme {
IconTheme::IconTheme(const std::string& name)
: index_theme_loaded_(false) {
- base::ThreadRestrictions::AssertIOAllowed();
+ 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;
@@ -256,7 +255,7 @@ FilePath IconTheme::GetIconPathUnderSubdir(const std::string& icon_name,
}
bool IconTheme::LoadIndexTheme(const FilePath& file) {
- FILE* fp = file_util::OpenFile(file, "r");
+ FILE* fp = base::OpenFile(file, "r");
SubDirInfo* current_info = NULL;
if (!fp)
return false;
@@ -281,7 +280,7 @@ bool IconTheme::LoadIndexTheme(const FilePath& file) {
std::string key, value;
std::vector<std::string> r;
- base::SplitStringDontTrim(entry, '=', &r);
+ SplitStringDontTrim(entry, '=', &r);
if (r.size() < 2)
continue;
@@ -317,7 +316,7 @@ bool IconTheme::LoadIndexTheme(const FilePath& file) {
}
}
- file_util::CloseFile(fp);
+ base::CloseFile(fp);
return info_array_.get() != NULL;
}
@@ -385,12 +384,11 @@ bool IconTheme::SetDirectories(const std::string& dirs) {
return true;
}
-bool CheckDirExistsAndGetMtime(const FilePath& dir,
- base::Time* last_modified) {
+bool CheckDirExistsAndGetMtime(const FilePath& dir, Time* last_modified) {
if (!DirectoryExists(dir))
return false;
- base::PlatformFileInfo file_info;
- if (!file_util::GetFileInfo(dir, &file_info))
+ PlatformFileInfo file_info;
+ if (!GetFileInfo(dir, &file_info))
return false;
*last_modified = file_info.last_modified;
return true;
@@ -398,7 +396,7 @@ bool CheckDirExistsAndGetMtime(const FilePath& dir,
// Make sure |dir| exists and add it to the list of icon directories.
void TryAddIconDir(const FilePath& dir) {
- base::Time last_modified;
+ Time last_modified;
if (!CheckDirExistsAndGetMtime(dir, &last_modified))
return;
MimeUtilConstants::GetInstance()->icon_dirs_[dir] = last_modified;
@@ -414,7 +412,7 @@ void AddXDGDataDir(const FilePath& dir) {
// Add all the xdg icon directories.
void InitIconDir() {
- FilePath home = file_util::GetHomeDir();
+ FilePath home = GetHomeDir();
if (!home.empty()) {
FilePath legacy_data_dir(home);
legacy_data_dir = legacy_data_dir.AppendASCII(".icons");
@@ -449,15 +447,15 @@ void InitIconDir() {
void EnsureUpdated() {
MimeUtilConstants* constants = MimeUtilConstants::GetInstance();
if (constants->last_check_time_.is_null()) {
- constants->last_check_time_ = base::TimeTicks::Now();
+ 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.
- base::TimeDelta time_since_last_check =
- base::TimeTicks::Now() - constants->last_check_time_;
+ 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;
@@ -465,7 +463,7 @@ void EnsureUpdated() {
MimeUtilConstants::IconDirMtimeMap* icon_dirs = &constants->icon_dirs_;
MimeUtilConstants::IconDirMtimeMap::iterator iter;
for (iter = icon_dirs->begin(); iter != icon_dirs->end(); ++iter) {
- base::Time last_modified;
+ Time last_modified;
if (!CheckDirExistsAndGetMtime(iter->first, &last_modified) ||
last_modified != iter->second) {
rescan_icon_dirs = true;
@@ -502,7 +500,7 @@ void InitDefaultThemes() {
IconTheme** default_themes =
MimeUtilConstants::GetInstance()->default_themes_;
- scoped_ptr<base::Environment> env(base::Environment::Create());
+ scoped_ptr<Environment> env(Environment::Create());
base::nix::DesktopEnvironment desktop_env =
base::nix::GetDesktopEnvironment(env.get());
if (desktop_env == base::nix::DESKTOP_ENVIRONMENT_KDE3 ||
@@ -577,14 +575,14 @@ MimeUtilConstants::~MimeUtilConstants() {
std::string GetFileMimeType(const FilePath& filepath) {
if (filepath.empty())
return std::string();
- base::ThreadRestrictions::AssertIOAllowed();
- base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ ThreadRestrictions::AssertIOAllowed();
+ AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
return xdg_mime_get_mime_type_from_file_name(filepath.value().c_str());
}
std::string GetDataMimeType(const std::string& data) {
- base::ThreadRestrictions::AssertIOAllowed();
- base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ ThreadRestrictions::AssertIOAllowed();
+ AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
return xdg_mime_get_mime_type_for_data(data.data(), data.length(), NULL);
}
@@ -599,13 +597,13 @@ void SetIconThemeName(const std::string& name) {
}
FilePath GetMimeIcon(const std::string& mime_type, size_t size) {
- base::ThreadRestrictions::AssertIOAllowed();
+ ThreadRestrictions::AssertIOAllowed();
std::vector<std::string> icon_names;
std::string icon_name;
FilePath icon_file;
if (!mime_type.empty()) {
- base::AutoLock scoped_lock(g_mime_util_xdg_lock.Get());
+ 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 : "");
}
diff --git a/chromium/base/nix/xdg_util.cc b/chromium/base/nix/xdg_util.cc
index b3caf2abe0b..4c1510f5c11 100644
--- a/chromium/base/nix/xdg_util.cc
+++ b/chromium/base/nix/xdg_util.cc
@@ -31,7 +31,7 @@ FilePath GetXDGDirectory(Environment* env, const char* env_name,
if (env->GetVar(env_name, &env_value) && !env_value.empty())
path = FilePath(env_value);
else
- path = file_util::GetHomeDir().Append(fallback_dir);
+ path = GetHomeDir().Append(fallback_dir);
return path.StripTrailingSeparators();
}
@@ -42,7 +42,7 @@ FilePath GetXDGUserDirectory(const char* dir_name, const char* fallback_dir) {
path = FilePath(xdg_dir);
free(xdg_dir);
} else {
- path = file_util::GetHomeDir().Append(fallback_dir);
+ path = GetHomeDir().Append(fallback_dir);
}
return path.StripTrailingSeparators();
}
diff --git a/chromium/base/observer_list.h b/chromium/base/observer_list.h
index b44e33f56c7..bd1dc64055b 100644
--- a/chromium/base/observer_list.h
+++ b/chromium/base/observer_list.h
@@ -203,14 +203,15 @@ class ObserverList : public ObserverListBase<ObserverType> {
}
};
-#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
- do { \
- if ((observer_list).might_have_observers()) { \
- ObserverListBase<ObserverType>::Iterator it(observer_list); \
- ObserverType* obs; \
- while ((obs = it.GetNext()) != NULL) \
- obs->func; \
- } \
+#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
+ do { \
+ if ((observer_list).might_have_observers()) { \
+ ObserverListBase<ObserverType>::Iterator \
+ it_inside_observer_macro(observer_list); \
+ ObserverType* obs; \
+ while ((obs = it_inside_observer_macro.GetNext()) != NULL) \
+ obs->func; \
+ } \
} while (0)
#endif // BASE_OBSERVER_LIST_H__
diff --git a/chromium/base/os_compat_android_unittest.cc b/chromium/base/os_compat_android_unittest.cc
index c749b6a223d..a1d1fb19c91 100644
--- a/chromium/base/os_compat_android_unittest.cc
+++ b/chromium/base/os_compat_android_unittest.cc
@@ -17,7 +17,7 @@ typedef testing::Test OsCompatAndroidTest;
// passes.
TEST_F(OsCompatAndroidTest, DISABLED_TestMkdTemp) {
FilePath tmp_dir;
- EXPECT_TRUE(file_util::GetTempDir(&tmp_dir));
+ EXPECT_TRUE(base::GetTempDir(&tmp_dir));
// Not six XXXXXX at the suffix of the path.
FilePath sub_dir = tmp_dir.Append("XX");
diff --git a/chromium/base/path_service.cc b/chromium/base/path_service.cc
index 89e58b2cdb1..f0a6a844158 100644
--- a/chromium/base/path_service.cc
+++ b/chromium/base/path_service.cc
@@ -254,7 +254,7 @@ bool PathService::OverrideAndCreateIfNeeded(int key,
// this to the absolute path because on POSIX, MakeAbsoluteFilePath fails
// if called on a non-existent path.
if (!base::PathExists(file_path) &&
- !file_util::CreateDirectory(file_path))
+ !base::CreateDirectory(file_path))
return false;
}
diff --git a/chromium/base/pickle.cc b/chromium/base/pickle.cc
index af3191b9d86..12a32378788 100644
--- a/chromium/base/pickle.cc
+++ b/chromium/base/pickle.cc
@@ -10,6 +10,9 @@
//------------------------------------------------------------------------------
+using base::char16;
+using base::string16;
+
// static
const int Pickle::kPayloadUnit = 64;
@@ -91,7 +94,15 @@ bool PickleIterator::ReadUInt64(uint64* result) {
}
bool PickleIterator::ReadFloat(float* result) {
- return ReadBuiltinType(result);
+ // crbug.com/315213
+ // The source data may not be properly aligned, and unaligned float reads
+ // cause SIGBUS on some ARM platforms, so force using memcpy to copy the data
+ // into the result.
+ const char* read_from = GetReadPointerAndAdvance<float>();
+ if (!read_from)
+ return false;
+ memcpy(result, read_from, sizeof(*result));
+ return true;
}
bool PickleIterator::ReadString(std::string* result) {
@@ -153,8 +164,8 @@ bool PickleIterator::ReadBytes(const char** data, int length) {
Pickle::Pickle()
: header_(NULL),
header_size_(sizeof(Header)),
- capacity_(0),
- variable_buffer_offset_(0) {
+ capacity_after_header_(0),
+ write_offset_(0) {
Resize(kPayloadUnit);
header_->payload_size = 0;
}
@@ -162,8 +173,8 @@ Pickle::Pickle()
Pickle::Pickle(int header_size)
: header_(NULL),
header_size_(AlignInt(header_size, sizeof(uint32))),
- capacity_(0),
- variable_buffer_offset_(0) {
+ capacity_after_header_(0),
+ write_offset_(0) {
DCHECK_GE(static_cast<size_t>(header_size), sizeof(Header));
DCHECK_LE(header_size, kPayloadUnit);
Resize(kPayloadUnit);
@@ -173,8 +184,8 @@ Pickle::Pickle(int header_size)
Pickle::Pickle(const char* data, int data_len)
: header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
header_size_(0),
- capacity_(kCapacityReadOnly),
- variable_buffer_offset_(0) {
+ capacity_after_header_(kCapacityReadOnly),
+ write_offset_(0) {
if (data_len >= static_cast<int>(sizeof(Header)))
header_size_ = data_len - header_->payload_size;
@@ -192,16 +203,15 @@ Pickle::Pickle(const char* data, int data_len)
Pickle::Pickle(const Pickle& other)
: header_(NULL),
header_size_(other.header_size_),
- capacity_(0),
- variable_buffer_offset_(other.variable_buffer_offset_) {
+ capacity_after_header_(0),
+ write_offset_(other.write_offset_) {
size_t payload_size = header_size_ + other.header_->payload_size;
- bool resized = Resize(payload_size);
- CHECK(resized); // Realloc failed.
+ Resize(payload_size);
memcpy(header_, other.header_, payload_size);
}
Pickle::~Pickle() {
- if (capacity_ != kCapacityReadOnly)
+ if (capacity_after_header_ != kCapacityReadOnly)
free(header_);
}
@@ -210,20 +220,19 @@ Pickle& Pickle::operator=(const Pickle& other) {
NOTREACHED();
return *this;
}
- if (capacity_ == kCapacityReadOnly) {
+ if (capacity_after_header_ == kCapacityReadOnly) {
header_ = NULL;
- capacity_ = 0;
+ capacity_after_header_ = 0;
}
if (header_size_ != other.header_size_) {
free(header_);
header_ = NULL;
header_size_ = other.header_size_;
}
- bool resized = Resize(other.header_size_ + other.header_->payload_size);
- CHECK(resized); // Realloc failed.
+ Resize(other.header_->payload_size);
memcpy(header_, other.header_,
other.header_size_ + other.header_->payload_size);
- variable_buffer_offset_ = other.variable_buffer_offset_;
+ write_offset_ = other.write_offset_;
return *this;
}
@@ -254,91 +263,31 @@ bool Pickle::WriteData(const char* data, int length) {
return length >= 0 && WriteInt(length) && WriteBytes(data, length);
}
-bool Pickle::WriteBytes(const void* data, int data_len) {
- DCHECK_NE(kCapacityReadOnly, capacity_) << "oops: pickle is readonly";
-
- char* dest = BeginWrite(data_len);
- if (!dest)
- return false;
-
- memcpy(dest, data, data_len);
-
- EndWrite(dest, data_len);
+bool Pickle::WriteBytes(const void* data, int length) {
+ WriteBytesCommon(data, length);
return true;
}
-char* Pickle::BeginWriteData(int length) {
- DCHECK_EQ(variable_buffer_offset_, 0U) <<
- "There can only be one variable buffer in a Pickle";
-
- if (length < 0 || !WriteInt(length))
- return NULL;
-
- char *data_ptr = BeginWrite(length);
- if (!data_ptr)
- return NULL;
-
- variable_buffer_offset_ =
- data_ptr - reinterpret_cast<char*>(header_) - sizeof(int);
-
- // EndWrite doesn't necessarily have to be called after the write operation,
- // so we call it here to pad out what the caller will eventually write.
- EndWrite(data_ptr, length);
- return data_ptr;
-}
-
-void Pickle::TrimWriteData(int new_length) {
- DCHECK_NE(variable_buffer_offset_, 0U);
-
- // Fetch the the variable buffer size
- int* cur_length = reinterpret_cast<int*>(
- reinterpret_cast<char*>(header_) + variable_buffer_offset_);
-
- if (new_length < 0 || new_length > *cur_length) {
- NOTREACHED() << "Invalid length in TrimWriteData.";
- return;
- }
-
- // Update the payload size and variable buffer size
- header_->payload_size -= (*cur_length - new_length);
- *cur_length = new_length;
-}
-
-char* Pickle::BeginWrite(size_t length) {
- // write at a uint32-aligned offset from the beginning of the header
- size_t offset = AlignInt(header_->payload_size, sizeof(uint32));
-
- size_t new_size = offset + length;
- size_t needed_size = header_size_ + new_size;
- if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size)))
- return NULL;
-
+void Pickle::Reserve(size_t length) {
+ size_t data_len = AlignInt(length, sizeof(uint32));
+ DCHECK_GE(data_len, length);
#ifdef ARCH_CPU_64_BITS
- DCHECK_LE(length, kuint32max);
+ DCHECK_LE(data_len, kuint32max);
#endif
-
- header_->payload_size = static_cast<uint32>(new_size);
- return mutable_payload() + offset;
+ DCHECK_LE(write_offset_, kuint32max - data_len);
+ size_t new_size = write_offset_ + data_len;
+ if (new_size > capacity_after_header_)
+ Resize(capacity_after_header_ * 2 + new_size);
}
-void Pickle::EndWrite(char* dest, int length) {
- // Zero-pad to keep tools like valgrind from complaining about uninitialized
- // memory.
- if (length % sizeof(uint32))
- memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
-}
-
-bool Pickle::Resize(size_t new_capacity) {
+void Pickle::Resize(size_t new_capacity) {
new_capacity = AlignInt(new_capacity, kPayloadUnit);
- CHECK_NE(capacity_, kCapacityReadOnly);
- void* p = realloc(header_, new_capacity);
- if (!p)
- return false;
-
+ CHECK_NE(capacity_after_header_, kCapacityReadOnly);
+ void* p = realloc(header_, header_size_ + new_capacity);
+ CHECK(p);
header_ = reinterpret_cast<Header*>(p);
- capacity_ = new_capacity;
- return true;
+ capacity_after_header_ = new_capacity;
}
// static
@@ -348,14 +297,41 @@ const char* Pickle::FindNext(size_t header_size,
DCHECK_EQ(header_size, AlignInt(header_size, sizeof(uint32)));
DCHECK_LE(header_size, static_cast<size_t>(kPayloadUnit));
- if (static_cast<size_t>(end - start) < sizeof(Header))
+ size_t length = static_cast<size_t>(end - start);
+ if (length < sizeof(Header))
return NULL;
const Header* hdr = reinterpret_cast<const Header*>(start);
- const char* payload_base = start + header_size;
- const char* payload_end = payload_base + hdr->payload_size;
- if (payload_end < payload_base)
+ if (length < header_size || length - header_size < hdr->payload_size)
return NULL;
+ return start + header_size + hdr->payload_size;
+}
+
+template <size_t length> void Pickle::WriteBytesStatic(const void* data) {
+ WriteBytesCommon(data, length);
+}
+
+template void Pickle::WriteBytesStatic<2>(const void* data);
+template void Pickle::WriteBytesStatic<4>(const void* data);
+template void Pickle::WriteBytesStatic<8>(const void* data);
+
+inline void Pickle::WriteBytesCommon(const void* data, size_t length) {
+ DCHECK_NE(kCapacityReadOnly, capacity_after_header_)
+ << "oops: pickle is readonly";
+ size_t data_len = AlignInt(length, sizeof(uint32));
+ DCHECK_GE(data_len, length);
+#ifdef ARCH_CPU_64_BITS
+ DCHECK_LE(data_len, kuint32max);
+#endif
+ DCHECK_LE(write_offset_, kuint32max - data_len);
+ size_t new_size = write_offset_ + data_len;
+ if (new_size > capacity_after_header_) {
+ Resize(std::max(capacity_after_header_ * 2, new_size));
+ }
- return (payload_end > end) ? NULL : payload_end;
+ 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);
+ write_offset_ = new_size;
}
diff --git a/chromium/base/pickle.h b/chromium/base/pickle.h
index 3de2886e5d4..dd34f54176c 100644
--- a/chromium/base/pickle.h
+++ b/chromium/base/pickle.h
@@ -37,7 +37,7 @@ class BASE_EXPORT PickleIterator {
bool ReadFloat(float* result) WARN_UNUSED_RESULT;
bool ReadString(std::string* result) WARN_UNUSED_RESULT;
bool ReadWString(std::wstring* result) WARN_UNUSED_RESULT;
- bool ReadString16(string16* result) WARN_UNUSED_RESULT;
+ bool ReadString16(base::string16* result) WARN_UNUSED_RESULT;
bool ReadData(const char** data, int* length) WARN_UNUSED_RESULT;
bool ReadBytes(const char** data, int length) WARN_UNUSED_RESULT;
@@ -138,57 +138,73 @@ class BASE_EXPORT Pickle {
// For compatibility, these older style read methods pass through to the
// PickleIterator methods.
// TODO(jbates) Remove these methods.
- bool ReadBool(PickleIterator* iter, bool* result) const {
+ bool ReadBool(PickleIterator* iter,
+ bool* result) const WARN_UNUSED_RESULT {
return iter->ReadBool(result);
}
- bool ReadInt(PickleIterator* iter, int* result) const {
+ bool ReadInt(PickleIterator* iter,
+ int* result) const WARN_UNUSED_RESULT {
return iter->ReadInt(result);
}
- bool ReadLong(PickleIterator* iter, long* result) const {
+ bool ReadLong(PickleIterator* iter,
+ long* result) const WARN_UNUSED_RESULT {
return iter->ReadLong(result);
}
- bool ReadUInt16(PickleIterator* iter, uint16* result) const {
+ bool ReadUInt16(PickleIterator* iter,
+ uint16* result) const WARN_UNUSED_RESULT {
return iter->ReadUInt16(result);
}
- bool ReadUInt32(PickleIterator* iter, uint32* result) const {
+ bool ReadUInt32(PickleIterator* iter,
+ uint32* result) const WARN_UNUSED_RESULT {
return iter->ReadUInt32(result);
}
- bool ReadInt64(PickleIterator* iter, int64* result) const {
+ bool ReadInt64(PickleIterator* iter,
+ int64* result) const WARN_UNUSED_RESULT {
return iter->ReadInt64(result);
}
- bool ReadUInt64(PickleIterator* iter, uint64* result) const {
+ bool ReadUInt64(PickleIterator* iter,
+ uint64* result) const WARN_UNUSED_RESULT {
return iter->ReadUInt64(result);
}
- bool ReadFloat(PickleIterator* iter, float* result) const {
+ bool ReadFloat(PickleIterator* iter,
+ float* result) const WARN_UNUSED_RESULT {
return iter->ReadFloat(result);
}
- bool ReadString(PickleIterator* iter, std::string* result) const {
+ bool ReadString(PickleIterator* iter,
+ std::string* result) const WARN_UNUSED_RESULT {
return iter->ReadString(result);
}
- bool ReadWString(PickleIterator* iter, std::wstring* result) const {
+ bool ReadWString(PickleIterator* iter,
+ std::wstring* result) const WARN_UNUSED_RESULT {
return iter->ReadWString(result);
}
- bool ReadString16(PickleIterator* iter, string16* result) const {
+ bool ReadString16(PickleIterator* iter,
+ base::string16* result) const WARN_UNUSED_RESULT {
return iter->ReadString16(result);
}
// A pointer to the data will be placed in *data, and the length will be
// placed in *length. This buffer will be into the message's buffer so will
// be scoped to the lifetime of the message (or until the message data is
// mutated).
- bool ReadData(PickleIterator* iter, const char** data, int* length) const {
+ bool ReadData(PickleIterator* iter,
+ const char** data,
+ int* length) const WARN_UNUSED_RESULT {
return iter->ReadData(data, length);
}
// A pointer to the data will be placed in *data. The caller specifies the
// number of bytes to read, and ReadBytes will validate this length. The
// returned buffer will be into the message's buffer so will be scoped to the
// lifetime of the message (or until the message data is mutated).
- bool ReadBytes(PickleIterator* iter, const char** data, int length) const {
+ bool ReadBytes(PickleIterator* iter,
+ const char** data,
+ int length) const WARN_UNUSED_RESULT {
return iter->ReadBytes(data, length);
}
// Safer version of ReadInt() checks for the result not being negative.
// Use it for reading the object sizes.
- bool ReadLength(PickleIterator* iter, int* result) const {
+ bool ReadLength(PickleIterator* iter,
+ int* result) const WARN_UNUSED_RESULT {
return iter->ReadLength(result);
}
@@ -200,7 +216,7 @@ class BASE_EXPORT Pickle {
return WriteInt(value ? 1 : 0);
}
bool WriteInt(int value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
// WARNING: DO NOT USE THIS METHOD IF PICKLES ARE PERSISTED IN ANY WAY.
// It will write whatever a "long" is on this architecture. On 32-bit
@@ -208,54 +224,38 @@ class BASE_EXPORT Pickle {
// pickles are still around after upgrading to 64-bit, or if they are copied
// between dissimilar systems, YOUR PICKLES WILL HAVE GONE BAD.
bool WriteLongUsingDangerousNonPortableLessPersistableForm(long value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteUInt16(uint16 value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteUInt32(uint32 value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteInt64(int64 value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteUInt64(uint64 value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteFloat(float value) {
- return WriteBytes(&value, sizeof(value));
+ return WritePOD(value);
}
bool WriteString(const std::string& value);
bool WriteWString(const std::wstring& value);
- bool WriteString16(const string16& value);
+ bool WriteString16(const base::string16& value);
// "Data" is a blob with a length. When you read it out you will be given the
// length. See also WriteBytes.
bool WriteData(const char* data, int length);
- // "Bytes" is a blob with no length. The caller must specify the lenght both
+ // "Bytes" is a blob with no length. The caller must specify the length both
// when reading and writing. It is normally used to serialize PoD types of a
// known size. See also WriteData.
- bool WriteBytes(const void* data, int data_len);
-
- // Same as WriteData, but allows the caller to write directly into the
- // Pickle. This saves a copy in cases where the data is not already
- // available in a buffer. The caller should take care to not write more
- // than the length it declares it will. Use ReadData to get the data.
- // Returns NULL on failure.
- //
- // The returned pointer will only be valid until the next write operation
- // on this Pickle.
- char* BeginWriteData(int length);
-
- // For Pickles which contain variable length buffers (e.g. those created
- // with BeginWriteData), the Pickle can
- // be 'trimmed' if the amount of data required is less than originally
- // requested. For example, you may have created a buffer with 10K of data,
- // but decided to only fill 10 bytes of that data. Use this function
- // to trim the buffer so that we don't send 9990 bytes of unused data.
- // You cannot increase the size of the variable buffer; only shrink it.
- // This function assumes that the length of the variable buffer has
- // not been changed.
- void TrimWriteData(int length);
+ bool WriteBytes(const void* data, int length);
+
+ // Reserves space for upcoming writes when multiple writes will be made and
+ // their sizes are computed in advance. It can be significantly faster to call
+ // Reserve() before calling WriteFoo() multiple times.
+ void Reserve(size_t additional_capacity);
// Payload follows after allocation of Header (header size is customizable).
struct Header {
@@ -295,26 +295,13 @@ class BASE_EXPORT Pickle {
return reinterpret_cast<char*>(header_) + header_size_;
}
- size_t capacity() const {
- return capacity_;
+ size_t capacity_after_header() const {
+ return capacity_after_header_;
}
- // Resizes the buffer for use when writing the specified amount of data. The
- // location that the data should be written at is returned, or NULL if there
- // was an error. Call EndWrite with the returned offset and the given length
- // to pad out for the next write.
- char* BeginWrite(size_t length);
-
- // Completes the write operation by padding the data with NULL bytes until it
- // is padded. Should be paired with BeginWrite, but it does not necessarily
- // have to be called after the data is written.
- void EndWrite(char* dest, int length);
-
- // Resize the capacity, note that the input value should include the size of
- // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
- // A realloc() failure will cause a Resize failure... and caller should check
- // the return result for true (i.e., successful resizing).
- bool Resize(size_t new_capacity);
+ // Resize the capacity, note that the input value should not include the size
+ // of the header.
+ void Resize(size_t new_capacity);
// Aligns 'i' by rounding it up to the next multiple of 'alignment'
static size_t AlignInt(size_t i, int alignment) {
@@ -335,13 +322,27 @@ class BASE_EXPORT Pickle {
Header* header_;
size_t header_size_; // Supports extra data between header and payload.
- // Allocation size of payload (or -1 if allocation is const).
- size_t capacity_;
- size_t variable_buffer_offset_; // IF non-zero, then offset to a buffer.
+ // Allocation size of payload (or -1 if allocation is const). Note: this
+ // doesn't count the header.
+ size_t capacity_after_header_;
+ // The offset at which we will write the next field. Note: this doesn't count
+ // the header.
+ size_t write_offset_;
+
+ // Just like WriteBytes, but with a compile-time size, for performance.
+ template<size_t length> void WriteBytesStatic(const void* data);
+
+ // Writes a POD by copying its bytes.
+ template <typename T> bool WritePOD(const T& data) {
+ WriteBytesStatic<sizeof(data)>(&data);
+ return true;
+ }
+ inline void WriteBytesCommon(const void* data, size_t length);
FRIEND_TEST_ALL_PREFIXES(PickleTest, Resize);
FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNext);
FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextWithIncompleteHeader);
+ FRIEND_TEST_ALL_PREFIXES(PickleTest, FindNextOverflow);
};
#endif // BASE_PICKLE_H__
diff --git a/chromium/base/pickle_unittest.cc b/chromium/base/pickle_unittest.cc
index cc384c70617..b1c5925f668 100644
--- a/chromium/base/pickle_unittest.cc
+++ b/chromium/base/pickle_unittest.cc
@@ -10,6 +10,9 @@
#include "base/strings/string16.h"
#include "testing/gtest/include/gtest/gtest.h"
+// Remove when this file is in the base namespace.
+using base::string16;
+
namespace {
const int testint = 2093847192;
@@ -58,10 +61,6 @@ void VerifyResult(const Pickle& pickle) {
EXPECT_EQ(testdatalen, outdatalen);
EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
- EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
- EXPECT_EQ(testdatalen, outdatalen);
- EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
-
// reads past the end should fail
EXPECT_FALSE(pickle.ReadInt(&iter, &outint));
}
@@ -79,14 +78,6 @@ TEST(PickleTest, EncodeDecode) {
EXPECT_TRUE(pickle.WriteUInt16(testuint16));
EXPECT_TRUE(pickle.WriteFloat(testfloat));
EXPECT_TRUE(pickle.WriteData(testdata, testdatalen));
-
- // Over allocate BeginWriteData so we can test TrimWriteData.
- char* dest = pickle.BeginWriteData(testdatalen + 100);
- EXPECT_TRUE(dest);
- memcpy(dest, testdata, testdatalen);
-
- pickle.TrimWriteData(testdatalen);
-
VerifyResult(pickle);
// test copy constructor
@@ -194,6 +185,37 @@ TEST(PickleTest, FindNextWithIncompleteHeader) {
EXPECT_TRUE(NULL == Pickle::FindNext(header_size, start, end));
}
+#if defined(COMPILER_MSVC)
+#pragma warning(push)
+#pragma warning(disable: 4146)
+#endif
+TEST(PickleTest, FindNextOverflow) {
+ size_t header_size = sizeof(Pickle::Header);
+ size_t header_size2 = 2 * header_size;
+ size_t payload_received = 100;
+ scoped_ptr<char[]> buffer(new char[header_size2 + payload_received]);
+ const char* start = buffer.get();
+ Pickle::Header* header = reinterpret_cast<Pickle::Header*>(buffer.get());
+ const char* end = start + header_size2 + payload_received;
+ // It is impossible to construct an overflow test otherwise.
+ if (sizeof(size_t) > sizeof(header->payload_size) ||
+ sizeof(uintptr_t) > sizeof(header->payload_size))
+ return;
+
+ header->payload_size = -(reinterpret_cast<uintptr_t>(start) + header_size2);
+ EXPECT_TRUE(NULL == Pickle::FindNext(header_size2, start, end));
+
+ header->payload_size = -header_size2;
+ EXPECT_TRUE(NULL == Pickle::FindNext(header_size2, start, end));
+
+ header->payload_size = 0;
+ end = start + header_size;
+ EXPECT_TRUE(NULL == Pickle::FindNext(header_size2, start, end));
+}
+#if defined(COMPILER_MSVC)
+#pragma warning(pop)
+#endif
+
TEST(PickleTest, GetReadPointerAndAdvance) {
Pickle pickle;
@@ -229,19 +251,19 @@ TEST(PickleTest, Resize) {
size_t cur_payload = payload_size_after_header;
// note: we assume 'unit' is a power of 2
- EXPECT_EQ(unit, pickle.capacity());
+ EXPECT_EQ(unit, pickle.capacity_after_header());
EXPECT_EQ(pickle.payload_size(), payload_size_after_header);
// fill out a full page (noting data header)
pickle.WriteData(data_ptr, static_cast<int>(unit - sizeof(uint32)));
cur_payload += unit;
- EXPECT_EQ(unit * 2, pickle.capacity());
+ EXPECT_EQ(unit * 2, pickle.capacity_after_header());
EXPECT_EQ(cur_payload, pickle.payload_size());
// one more byte should double the capacity
pickle.WriteData(data_ptr, 1);
cur_payload += 5;
- EXPECT_EQ(unit * 4, pickle.capacity());
+ EXPECT_EQ(unit * 4, pickle.capacity_after_header());
EXPECT_EQ(cur_payload, pickle.payload_size());
}
diff --git a/chromium/base/platform_file.h b/chromium/base/platform_file.h
index e94c7ed0e3f..d9a82cbd3f4 100644
--- a/chromium/base/platform_file.h
+++ b/chromium/base/platform_file.h
@@ -19,11 +19,18 @@
namespace base {
+// ***************************************************************************
+// ***** Don't use anything from this file anymore. It is being removed!
+// ***** Use base/files/base_file.h instead
+// ***************************************************************************
+
// PLATFORM_FILE_(OPEN|CREATE).* are mutually exclusive. You should specify
// exactly one of the five (possibly combining with other flags) when opening
// or creating a file.
// PLATFORM_FILE_(WRITE|APPEND) are mutually exclusive. This is so that APPEND
// behavior will be consistent with O_APPEND on POSIX.
+// PLATFORM_FILE_EXCLUSIVE_(READ|WRITE) only grant exclusive access to the file
+// on creation on POSIX; for existing files, consider using LockPlatformFile().
enum PlatformFileFlags {
PLATFORM_FILE_OPEN = 1 << 0, // Opens a file, only if it exists.
PLATFORM_FILE_CREATE = 1 << 1, // Creates a new file, only if it
@@ -44,16 +51,19 @@ enum PlatformFileFlags {
PLATFORM_FILE_DELETE_ON_CLOSE = 1 << 13,
PLATFORM_FILE_WRITE_ATTRIBUTES = 1 << 14, // Used on Windows only
- PLATFORM_FILE_ENUMERATE = 1 << 15, // May enumerate directory
- PLATFORM_FILE_SHARE_DELETE = 1 << 16, // Used on Windows only
+ PLATFORM_FILE_SHARE_DELETE = 1 << 15, // Used on Windows only
- PLATFORM_FILE_TERMINAL_DEVICE = 1 << 17, // Serial port flags
- PLATFORM_FILE_BACKUP_SEMANTICS = 1 << 18, // Used on Windows only
+ PLATFORM_FILE_TERMINAL_DEVICE = 1 << 16, // Serial port flags
+ PLATFORM_FILE_BACKUP_SEMANTICS = 1 << 17, // Used on Windows only
- PLATFORM_FILE_EXECUTE = 1 << 19, // Used on Windows only
+ PLATFORM_FILE_EXECUTE = 1 << 18, // Used on Windows only
};
+// This enum has been recorded in multiple histograms. If the order of the
+// fields needs to change, please ensure that those histograms are obsolete or
+// have been moved to a different enum.
+//
// PLATFORM_FILE_ERROR_ACCESS_DENIED is returned when a call fails because of
// a filesystem restriction. PLATFORM_FILE_ERROR_SECURITY is returned when a
// browser policy doesn't allow the operation to be executed.
@@ -117,11 +127,11 @@ struct BASE_EXPORT PlatformFileInfo {
#if defined(OS_WIN)
typedef HANDLE PlatformFile;
const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE;
-PlatformFileError LastErrorToPlatformFileError(DWORD saved_errno);
+BASE_EXPORT PlatformFileError LastErrorToPlatformFileError(DWORD last_error);
#elif defined(OS_POSIX)
typedef int PlatformFile;
const PlatformFile kInvalidPlatformFileValue = -1;
-PlatformFileError ErrnoToPlatformFileError(int saved_errno);
+BASE_EXPORT PlatformFileError ErrnoToPlatformFileError(int saved_errno);
#endif
// Creates or opens the given file. If |created| is provided, it will be set to
@@ -211,6 +221,31 @@ BASE_EXPORT bool TouchPlatformFile(PlatformFile file,
// Returns some information for the given file.
BASE_EXPORT bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info);
+// Attempts to take an exclusive write lock on the file. Returns immediately
+// (i.e. does not wait for another process to unlock the file). If the lock
+// was obtained, the result will be PLATFORM_FILE_OK. A lock only guarantees
+// that other processes may not also take a lock on the same file with the
+// same API - it may still be opened, renamed, unlinked, etc.
+//
+// Common semantics:
+// * Locks are held by processes, but not inherited by child processes.
+// * Locks are released by the OS on file handle close or process termination.
+// * Locks are reliable only on local filesystems.
+// * Duplicated file handles may also write to locked files.
+// Windows-specific semantics:
+// * Locks are mandatory for read/write APIs, advisory for mapping APIs.
+// * Within a process, locking the same file (by the same or new handle)
+// will fail.
+// POSIX-specific semantics:
+// * Locks are advisory only.
+// * Within a process, locking the same file (by the same or new handle)
+// will succeed.
+// * Closing any descriptor on a given file releases the lock.
+BASE_EXPORT PlatformFileError LockPlatformFile(PlatformFile file);
+
+// Unlock a file previously locked with LockPlatformFile.
+BASE_EXPORT PlatformFileError UnlockPlatformFile(PlatformFile file);
+
// Use this class to pass ownership of a PlatformFile to a receiver that may or
// may not want to accept it. This class does not own the storage for the
// PlatformFile.
diff --git a/chromium/base/platform_file_posix.cc b/chromium/base/platform_file_posix.cc
index 056577c5817..028a38276d6 100644
--- a/chromium/base/platform_file_posix.cc
+++ b/chromium/base/platform_file_posix.cc
@@ -46,15 +46,6 @@ static int CallFstat(int fd, stat_wrapper_t *sb) {
// NaCl doesn't provide the following system calls, so either simulate them or
// wrap them in order to minimize the number of #ifdef's in this file.
#if !defined(OS_NACL)
-static int DoPread(PlatformFile file, char* data, int size, int64 offset) {
- return HANDLE_EINTR(pread(file, data, size, offset));
-}
-
-static int DoPwrite(PlatformFile file, const char* data, int size,
- int64 offset) {
- return HANDLE_EINTR(pwrite(file, data, size, offset));
-}
-
static bool IsOpenAppend(PlatformFile file) {
return (fcntl(file, F_GETFL) & O_APPEND) != 0;
}
@@ -83,18 +74,18 @@ static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
return futimes(file, times);
#endif
}
-#else // defined(OS_NACL)
-// TODO(bbudge) Remove DoPread, DoPwrite when NaCl implements pread, pwrite.
-static int DoPread(PlatformFile file, char* data, int size, int64 offset) {
- lseek(file, static_cast<off_t>(offset), SEEK_SET);
- return HANDLE_EINTR(read(file, data, size));
-}
-static int DoPwrite(PlatformFile file, const char* data, int size,
- int64 offset) {
- lseek(file, static_cast<off_t>(offset), SEEK_SET);
- return HANDLE_EINTR(write(file, data, size));
+static PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0; // Lock entire file.
+ if (HANDLE_EINTR(fcntl(file, do_lock ? F_SETLK : F_UNLCK, &lock)) == -1)
+ return ErrnoToPlatformFileError(errno);
+ return PLATFORM_FILE_OK;
}
+#else // defined(OS_NACL)
static bool IsOpenAppend(PlatformFile file) {
// NaCl doesn't implement fcntl. Since NaCl's write conforms to the POSIX
@@ -117,6 +108,11 @@ static int CallFutimes(PlatformFile file, const struct timeval times[2]) {
NOTIMPLEMENTED(); // NaCl doesn't implement futimes.
return 0;
}
+
+static PlatformFileError CallFctnlFlock(PlatformFile file, bool do_lock) {
+ NOTIMPLEMENTED(); // NaCl doesn't implement flock struct.
+ return PLATFORM_FILE_ERROR_INVALID_OPERATION;
+}
#endif // defined(OS_NACL)
} // namespace
@@ -225,7 +221,7 @@ FILE* FdopenPlatformFile(PlatformFile file, const char* mode) {
bool ClosePlatformFile(PlatformFile file) {
base::ThreadRestrictions::AssertIOAllowed();
- return !HANDLE_EINTR(close(file));
+ return !IGNORE_EINTR(close(file));
}
int64 SeekPlatformFile(PlatformFile file,
@@ -246,8 +242,8 @@ int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
int bytes_read = 0;
int rv;
do {
- rv = DoPread(file, data + bytes_read,
- size - bytes_read, offset + bytes_read);
+ rv = HANDLE_EINTR(pread(file, data + bytes_read,
+ size - bytes_read, offset + bytes_read));
if (rv <= 0)
break;
@@ -281,7 +277,7 @@ int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset,
if (file < 0)
return -1;
- return DoPread(file, data, size, offset);
+ return HANDLE_EINTR(pread(file, data, size, offset));
}
int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
@@ -306,8 +302,8 @@ int WritePlatformFile(PlatformFile file, int64 offset,
int bytes_written = 0;
int rv;
do {
- rv = DoPwrite(file, data + bytes_written,
- size - bytes_written, offset + bytes_written);
+ rv = HANDLE_EINTR(pwrite(file, data + bytes_written,
+ size - bytes_written, offset + bytes_written));
if (rv <= 0)
break;
@@ -426,6 +422,14 @@ bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
return true;
}
+PlatformFileError LockPlatformFile(PlatformFile file) {
+ return CallFctnlFlock(file, true);
+}
+
+PlatformFileError UnlockPlatformFile(PlatformFile file) {
+ return CallFctnlFlock(file, false);
+}
+
PlatformFileError ErrnoToPlatformFileError(int saved_errno) {
switch (saved_errno) {
case EACCES:
diff --git a/chromium/base/platform_file_unittest.cc b/chromium/base/platform_file_unittest.cc
index a1f3927eddd..9cf66a9369a 100644
--- a/chromium/base/platform_file_unittest.cc
+++ b/chromium/base/platform_file_unittest.cc
@@ -8,151 +8,151 @@
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
-using base::FilePath;
+namespace base {
namespace {
// Reads from a file the given number of bytes, or until EOF is reached.
// Returns the number of bytes read.
-int ReadFully(base::PlatformFile file, int64 offset, char* data, int size) {
- return base::ReadPlatformFile(file, offset, data, size);
+int ReadFully(PlatformFile file, int64 offset, char* data, int size) {
+ return ReadPlatformFile(file, offset, data, size);
}
// Writes the given number of bytes to a file.
// Returns the number of bytes written.
-int WriteFully(base::PlatformFile file, int64 offset,
+int WriteFully(PlatformFile file, int64 offset,
const char* data, int size) {
- return base::WritePlatformFile(file, offset, data, size);
+ return WritePlatformFile(file, offset, data, size);
}
} // namespace
TEST(PlatformFile, CreatePlatformFile) {
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
// Open a file that doesn't exist.
- base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFileError error_code = PLATFORM_FILE_OK;
+ PlatformFile file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
NULL,
&error_code);
- EXPECT_EQ(base::kInvalidPlatformFileValue, file);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_NOT_FOUND, error_code);
+ EXPECT_EQ(kInvalidPlatformFileValue, file);
+ EXPECT_EQ(PLATFORM_FILE_ERROR_NOT_FOUND, error_code);
// Open or create a file.
bool created = false;
- error_code = base::PLATFORM_FILE_OK;
- file = base::CreatePlatformFile(
+ error_code = PLATFORM_FILE_OK;
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_OPEN_ALWAYS | PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_TRUE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
- base::ClosePlatformFile(file);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
+ ClosePlatformFile(file);
// Open an existing file.
created = false;
- file = base::CreatePlatformFile(
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_FALSE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
- base::ClosePlatformFile(file);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
+ ClosePlatformFile(file);
// Create a file that exists.
- file = base::CreatePlatformFile(
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_EQ(base::kInvalidPlatformFileValue, file);
+ EXPECT_EQ(kInvalidPlatformFileValue, file);
EXPECT_FALSE(created);
- EXPECT_EQ(base::PLATFORM_FILE_ERROR_EXISTS, error_code);
+ EXPECT_EQ(PLATFORM_FILE_ERROR_EXISTS, error_code);
// Create or overwrite a file.
- error_code = base::PLATFORM_FILE_OK;
- file = base::CreatePlatformFile(
+ error_code = PLATFORM_FILE_OK;
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_CREATE_ALWAYS | PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_TRUE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
- base::ClosePlatformFile(file);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
+ ClosePlatformFile(file);
// Create a delete-on-close file.
created = false;
file_path = temp_dir.path().AppendASCII("create_file_2");
- file = base::CreatePlatformFile(
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_DELETE_ON_CLOSE |
- base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_OPEN_ALWAYS | PLATFORM_FILE_DELETE_ON_CLOSE |
+ PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_TRUE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
- EXPECT_TRUE(base::ClosePlatformFile(file));
- EXPECT_FALSE(base::PathExists(file_path));
+ EXPECT_TRUE(ClosePlatformFile(file));
+ EXPECT_FALSE(PathExists(file_path));
}
TEST(PlatformFile, DeleteOpenFile) {
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("create_file_1");
// Create a file.
bool created = false;
- base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFileError error_code = PLATFORM_FILE_OK;
+ PlatformFile file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN_ALWAYS | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_SHARE_DELETE,
+ PLATFORM_FILE_OPEN_ALWAYS | PLATFORM_FILE_READ |
+ PLATFORM_FILE_SHARE_DELETE,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_TRUE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
// Open an existing file and mark it as delete on close.
created = false;
- base::PlatformFile same_file = base::CreatePlatformFile(
+ PlatformFile same_file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_DELETE_ON_CLOSE |
- base::PLATFORM_FILE_READ,
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_DELETE_ON_CLOSE |
+ PLATFORM_FILE_READ,
&created,
&error_code);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
EXPECT_FALSE(created);
- EXPECT_EQ(base::PLATFORM_FILE_OK, error_code);
+ EXPECT_EQ(PLATFORM_FILE_OK, error_code);
// Close both handles and check that the file is gone.
- base::ClosePlatformFile(file);
- base::ClosePlatformFile(same_file);
- EXPECT_FALSE(base::PathExists(file_path));
+ ClosePlatformFile(file);
+ ClosePlatformFile(same_file);
+ EXPECT_FALSE(PathExists(file_path));
}
TEST(PlatformFile, ReadWritePlatformFile) {
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("read_write_file");
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFile file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_READ |
+ PLATFORM_FILE_WRITE,
NULL,
NULL);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
char data_to_write[] = "test";
const int kTestDataSize = 4;
@@ -188,7 +188,7 @@ TEST(PlatformFile, ReadWritePlatformFile) {
EXPECT_EQ(data_to_write[i], data_read_1[i]);
// Read again, but using the trivial native wrapper.
- bytes_read = base::ReadPlatformFileNoBestEffort(file, 0, data_read_1,
+ bytes_read = ReadPlatformFileNoBestEffort(file, 0, data_read_1,
kTestDataSize);
EXPECT_LE(bytes_read, kTestDataSize);
for (int i = 0; i < bytes_read; i++)
@@ -203,7 +203,7 @@ TEST(PlatformFile, ReadWritePlatformFile) {
// Make sure the file was extended.
int64 file_size = 0;
- EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
EXPECT_EQ(kOffsetBeyondEndOfFile + kPartialWriteLength, file_size);
// Make sure the file was zero-padded.
@@ -218,19 +218,19 @@ TEST(PlatformFile, ReadWritePlatformFile) {
EXPECT_EQ(data_to_write[i - kOffsetBeyondEndOfFile], data_read_2[i]);
// Close the file handle to allow the temp directory to be deleted.
- base::ClosePlatformFile(file);
+ ClosePlatformFile(file);
}
TEST(PlatformFile, AppendPlatformFile) {
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("append_file");
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFile file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_APPEND,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_APPEND,
NULL,
NULL);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
char data_to_write[] = "test";
const int kTestDataSize = 4;
@@ -243,14 +243,14 @@ TEST(PlatformFile, AppendPlatformFile) {
bytes_written = WriteFully(file, 0, data_to_write, kTestDataSize);
EXPECT_EQ(kTestDataSize, bytes_written);
- base::ClosePlatformFile(file);
- file = base::CreatePlatformFile(
+ ClosePlatformFile(file);
+ file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_APPEND,
+ PLATFORM_FILE_OPEN | PLATFORM_FILE_READ |
+ PLATFORM_FILE_APPEND,
NULL,
NULL);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
char append_data_to_write[] = "78";
const int kAppendDataSize = 2;
@@ -270,21 +270,21 @@ TEST(PlatformFile, AppendPlatformFile) {
EXPECT_EQ(append_data_to_write[i], data_read_1[kTestDataSize + i]);
// Close the file handle to allow the temp directory to be deleted.
- base::ClosePlatformFile(file);
+ ClosePlatformFile(file);
}
TEST(PlatformFile, TruncatePlatformFile) {
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath file_path = temp_dir.path().AppendASCII("truncate_file");
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFile file = CreatePlatformFile(
file_path,
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ |
- base::PLATFORM_FILE_WRITE,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_READ |
+ PLATFORM_FILE_WRITE,
NULL,
NULL);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
// Write "test" to the file.
char data_to_write[] = "test";
@@ -295,8 +295,8 @@ TEST(PlatformFile, TruncatePlatformFile) {
// Extend the file.
const int kExtendedFileLength = 10;
int64 file_size = 0;
- EXPECT_TRUE(base::TruncatePlatformFile(file, kExtendedFileLength));
- EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_TRUE(TruncatePlatformFile(file, kExtendedFileLength));
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
EXPECT_EQ(kExtendedFileLength, file_size);
// Make sure the file was zero-padded.
@@ -310,8 +310,8 @@ TEST(PlatformFile, TruncatePlatformFile) {
// Truncate the file.
const int kTruncatedFileLength = 2;
- EXPECT_TRUE(base::TruncatePlatformFile(file, kTruncatedFileLength));
- EXPECT_TRUE(file_util::GetFileSize(file_path, &file_size));
+ EXPECT_TRUE(TruncatePlatformFile(file, kTruncatedFileLength));
+ EXPECT_TRUE(GetFileSize(file_path, &file_size));
EXPECT_EQ(kTruncatedFileLength, file_size);
// Make sure the file was truncated.
@@ -321,7 +321,7 @@ TEST(PlatformFile, TruncatePlatformFile) {
EXPECT_EQ(data_to_write[i], data_read[i]);
// Close the file handle to allow the temp directory to be deleted.
- base::ClosePlatformFile(file);
+ ClosePlatformFile(file);
}
// Flakily fails: http://crbug.com/86494
@@ -330,30 +330,30 @@ TEST(PlatformFile, TouchGetInfoPlatformFile) {
#else
TEST(PlatformFile, DISABLED_TouchGetInfoPlatformFile) {
#endif
- base::ScopedTempDir temp_dir;
+ ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- base::PlatformFile file = base::CreatePlatformFile(
+ PlatformFile file = CreatePlatformFile(
temp_dir.path().AppendASCII("touch_get_info_file"),
- base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE |
- base::PLATFORM_FILE_WRITE_ATTRIBUTES,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_WRITE |
+ PLATFORM_FILE_WRITE_ATTRIBUTES,
NULL,
NULL);
- EXPECT_NE(base::kInvalidPlatformFileValue, file);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
// Get info for a newly created file.
- base::PlatformFileInfo info;
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+ PlatformFileInfo info;
+ EXPECT_TRUE(GetPlatformFileInfo(file, &info));
// Add 2 seconds to account for possible rounding errors on
// filesystems that use a 1s or 2s timestamp granularity.
- base::Time now = base::Time::Now() + base::TimeDelta::FromSeconds(2);
+ Time now = Time::Now() + TimeDelta::FromSeconds(2);
EXPECT_EQ(0, info.size);
EXPECT_FALSE(info.is_directory);
EXPECT_FALSE(info.is_symbolic_link);
EXPECT_LE(info.last_accessed.ToInternalValue(), now.ToInternalValue());
EXPECT_LE(info.last_modified.ToInternalValue(), now.ToInternalValue());
EXPECT_LE(info.creation_time.ToInternalValue(), now.ToInternalValue());
- base::Time creation_time = info.creation_time;
+ Time creation_time = info.creation_time;
// Write "test" to the file.
char data[] = "test";
@@ -365,16 +365,15 @@ TEST(PlatformFile, DISABLED_TouchGetInfoPlatformFile) {
// It's best to add values that are multiples of 2 (in seconds)
// to the current last_accessed and last_modified times, because
// FATxx uses a 2s timestamp granularity.
- base::Time new_last_accessed =
- info.last_accessed + base::TimeDelta::FromSeconds(234);
- base::Time new_last_modified =
- info.last_modified + base::TimeDelta::FromMinutes(567);
+ Time new_last_accessed =
+ info.last_accessed + TimeDelta::FromSeconds(234);
+ Time new_last_modified =
+ info.last_modified + TimeDelta::FromMinutes(567);
- EXPECT_TRUE(base::TouchPlatformFile(file, new_last_accessed,
- new_last_modified));
+ EXPECT_TRUE(TouchPlatformFile(file, new_last_accessed, new_last_modified));
// Make sure the file info was updated accordingly.
- EXPECT_TRUE(base::GetPlatformFileInfo(file, &info));
+ EXPECT_TRUE(GetPlatformFileInfo(file, &info));
EXPECT_EQ(info.size, kTestDataSize);
EXPECT_FALSE(info.is_directory);
EXPECT_FALSE(info.is_symbolic_link);
@@ -396,5 +395,40 @@ TEST(PlatformFile, DISABLED_TouchGetInfoPlatformFile) {
creation_time.ToInternalValue());
// Close the file handle to allow the temp directory to be deleted.
- base::ClosePlatformFile(file);
+ ClosePlatformFile(file);
}
+
+TEST(PlatformFile, ReadFileAtCurrentPosition) {
+ ScopedTempDir temp_dir;
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+ FilePath file_path =
+ temp_dir.path().AppendASCII("read_file_at_current_position");
+ PlatformFile file = CreatePlatformFile(
+ file_path,
+ PLATFORM_FILE_CREATE | PLATFORM_FILE_READ |
+ PLATFORM_FILE_WRITE,
+ NULL, NULL);
+ EXPECT_NE(kInvalidPlatformFileValue, file);
+
+ const char kData[] = "test";
+ const int kDataSize = arraysize(kData) - 1;
+ EXPECT_EQ(kDataSize, WriteFully(file, 0, kData, kDataSize));
+
+ EXPECT_EQ(0, SeekPlatformFile(file, PLATFORM_FILE_FROM_BEGIN, 0));
+
+ char buffer[kDataSize];
+ int first_chunk_size = kDataSize / 2;
+ EXPECT_EQ(first_chunk_size,
+ ReadPlatformFileAtCurrentPos(
+ file, buffer, first_chunk_size));
+ EXPECT_EQ(kDataSize - first_chunk_size,
+ ReadPlatformFileAtCurrentPos(
+ file, buffer + first_chunk_size,
+ kDataSize - first_chunk_size));
+ EXPECT_EQ(std::string(buffer, buffer + kDataSize),
+ std::string(kData));
+
+ ClosePlatformFile(file);
+}
+
+} // namespace base
diff --git a/chromium/base/platform_file_win.cc b/chromium/base/platform_file_win.cc
index c5b49fa7e88..07b5c48c22b 100644
--- a/chromium/base/platform_file_win.cc
+++ b/chromium/base/platform_file_win.cc
@@ -134,7 +134,7 @@ int64 SeekPlatformFile(PlatformFile file,
int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
base::ThreadRestrictions::AssertIOAllowed();
- if (file == kInvalidPlatformFileValue)
+ if (file == kInvalidPlatformFileValue || size < 0)
return -1;
LARGE_INTEGER offset_li;
@@ -147,14 +147,24 @@ int ReadPlatformFile(PlatformFile file, int64 offset, char* data, int size) {
DWORD bytes_read;
if (::ReadFile(file, data, size, &bytes_read, &overlapped) != 0)
return bytes_read;
- else if (ERROR_HANDLE_EOF == GetLastError())
+ if (ERROR_HANDLE_EOF == GetLastError())
return 0;
return -1;
}
int ReadPlatformFileAtCurrentPos(PlatformFile file, char* data, int size) {
- return ReadPlatformFile(file, 0, data, size);
+ base::ThreadRestrictions::AssertIOAllowed();
+ if (file == kInvalidPlatformFileValue || size < 0)
+ return -1;
+
+ DWORD bytes_read;
+ if (::ReadFile(file, data, size, &bytes_read, NULL) != 0)
+ return bytes_read;
+ if (ERROR_HANDLE_EOF == GetLastError())
+ return 0;
+
+ return -1;
}
int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data,
@@ -164,7 +174,7 @@ int ReadPlatformFileNoBestEffort(PlatformFile file, int64 offset, char* data,
int ReadPlatformFileCurPosNoBestEffort(PlatformFile file,
char* data, int size) {
- return ReadPlatformFile(file, 0, data, size);
+ return ReadPlatformFileAtCurrentPos(file, data, size);
}
int WritePlatformFile(PlatformFile file, int64 offset,
@@ -262,6 +272,20 @@ bool GetPlatformFileInfo(PlatformFile file, PlatformFileInfo* info) {
return true;
}
+PlatformFileError LockPlatformFile(PlatformFile file) {
+ BOOL result = LockFile(file, 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return LastErrorToPlatformFileError(GetLastError());
+ return PLATFORM_FILE_OK;
+}
+
+PlatformFileError UnlockPlatformFile(PlatformFile file) {
+ BOOL result = UnlockFile(file, 0, 0, MAXDWORD, MAXDWORD);
+ if (!result)
+ return LastErrorToPlatformFileError(GetLastError());
+ return PLATFORM_FILE_OK;
+}
+
PlatformFileError LastErrorToPlatformFileError(DWORD last_error) {
switch (last_error) {
case ERROR_SHARING_VIOLATION:
diff --git a/chromium/base/posix/eintr_wrapper.h b/chromium/base/posix/eintr_wrapper.h
index 8e267523370..854c43a67cb 100644
--- a/chromium/base/posix/eintr_wrapper.h
+++ b/chromium/base/posix/eintr_wrapper.h
@@ -9,6 +9,9 @@
// caller will nonetheless see an EINTR in Debug builds.
//
// On Windows, this wrapper macro does nothing.
+//
+// Don't wrap close calls in HANDLE_EINTR. Use IGNORE_EINTR if the return
+// value of close is significant. See http://crbug.com/269623.
#ifndef BASE_POSIX_EINTR_WRAPPER_H_
#define BASE_POSIX_EINTR_WRAPPER_H_
@@ -20,6 +23,7 @@
#include <errno.h>
#if defined(NDEBUG)
+
#define HANDLE_EINTR(x) ({ \
typeof(x) eintr_wrapper_result; \
do { \
@@ -42,9 +46,21 @@
#endif // NDEBUG
+#define IGNORE_EINTR(x) ({ \
+ typeof(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ if (eintr_wrapper_result == -1 && errno == EINTR) { \
+ eintr_wrapper_result = 0; \
+ } \
+ } while (0); \
+ eintr_wrapper_result; \
+})
+
#else
#define HANDLE_EINTR(x) (x)
+#define IGNORE_EINTR(x) (x)
#endif // OS_POSIX
diff --git a/chromium/base/posix/file_descriptor_shuffle.cc b/chromium/base/posix/file_descriptor_shuffle.cc
index b5b7339bdfa..7bc9e26eb58 100644
--- a/chromium/base/posix/file_descriptor_shuffle.cc
+++ b/chromium/base/posix/file_descriptor_shuffle.cc
@@ -89,7 +89,7 @@ bool FileDescriptorTableInjection::Move(int src, int dest) {
}
void FileDescriptorTableInjection::Close(int fd) {
- int ret = HANDLE_EINTR(close(fd));
+ int ret = IGNORE_EINTR(close(fd));
DPCHECK(ret == 0);
}
diff --git a/chromium/base/posix/unix_domain_socket_linux_unittest.cc b/chromium/base/posix/unix_domain_socket_linux_unittest.cc
index 1343555b330..22bb172ad5b 100644
--- a/chromium/base/posix/unix_domain_socket_linux_unittest.cc
+++ b/chromium/base/posix/unix_domain_socket_linux_unittest.cc
@@ -45,7 +45,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAbortOnReplyFDClose) {
ASSERT_EQ(1U, message_fds.size());
// Close the reply FD.
- ASSERT_EQ(0, HANDLE_EINTR(close(message_fds.front())));
+ ASSERT_EQ(0, IGNORE_EINTR(close(message_fds.front())));
// Check that the thread didn't get blocked.
WaitableEvent event(false, false);
@@ -63,7 +63,7 @@ TEST(UnixDomainSocketTest, SendRecvMsgAvoidsSIGPIPE) {
int fds[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds));
file_util::ScopedFD scoped_fd1(&fds[1]);
- ASSERT_EQ(0, HANDLE_EINTR(close(fds[0])));
+ ASSERT_EQ(0, IGNORE_EINTR(close(fds[0])));
// Have the thread send a synchronous message via the socket. Unless the
// message is sent with MSG_NOSIGNAL, this shall result in SIGPIPE.
diff --git a/chromium/base/power_monitor/power_monitor_device_source.h b/chromium/base/power_monitor/power_monitor_device_source.h
index 993956031c8..37b065a77c6 100644
--- a/chromium/base/power_monitor/power_monitor_device_source.h
+++ b/chromium/base/power_monitor/power_monitor_device_source.h
@@ -61,9 +61,6 @@ class BASE_EXPORT PowerMonitorDeviceSource : public PowerMonitorSource {
~PowerMessageWindow();
private:
- void ProcessWmPowerBroadcastMessage(int event_id);
- LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
- WPARAM wparam, LPARAM lparam);
static LRESULT CALLBACK WndProcThunk(HWND hwnd,
UINT message,
WPARAM wparam,
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 6f4c1319b32..6609bd0e9e0 100644
--- a/chromium/base/power_monitor/power_monitor_device_source_win.cc
+++ b/chromium/base/power_monitor/power_monitor_device_source_win.cc
@@ -17,6 +17,37 @@ namespace {
const wchar_t kWindowClassName[] = L"Base_PowerMessageWindow";
+void ProcessWmPowerBroadcastMessage(WPARAM event_id) {
+ PowerMonitorSource::PowerEvent power_event;
+ switch (event_id) {
+ case PBT_APMPOWERSTATUSCHANGE: // The power status changed.
+ power_event = PowerMonitorSource::POWER_STATE_EVENT;
+ break;
+ case PBT_APMRESUMEAUTOMATIC: // Resume from suspend.
+ //case PBT_APMRESUMESUSPEND: // User-initiated resume from suspend.
+ // We don't notify for this latter event
+ // because if it occurs it is always sent as a
+ // second event after PBT_APMRESUMEAUTOMATIC.
+ power_event = PowerMonitorSource::RESUME_EVENT;
+ break;
+ case PBT_APMSUSPEND: // System has been suspended.
+ power_event = PowerMonitorSource::SUSPEND_EVENT;
+ break;
+ default:
+ return;
+
+ // Other Power Events:
+ // PBT_APMBATTERYLOW - removed in Vista.
+ // PBT_APMOEMEVENT - removed in Vista.
+ // PBT_APMQUERYSUSPEND - removed in Vista.
+ // PBT_APMQUERYSUSPENDFAILED - removed in Vista.
+ // PBT_APMRESUMECRITICAL - removed in Vista.
+ // PBT_POWERSETTINGCHANGE - user changed the power settings.
+ }
+
+ ProcessPowerEventHelper(power_event);
+}
+
} // namespace
// Function to query the system to see if it is currently running on
@@ -52,8 +83,6 @@ PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow()
message_hwnd_ = CreateWindowEx(WS_EX_NOACTIVATE, kWindowClassName,
NULL, WS_POPUP, 0, 0, 0, 0, NULL, NULL, instance_, NULL);
- SetWindowLongPtr(message_hwnd_, GWLP_USERDATA,
- reinterpret_cast<LONG_PTR>(this));
}
PowerMonitorDeviceSource::PowerMessageWindow::~PowerMessageWindow() {
@@ -63,68 +92,19 @@ PowerMonitorDeviceSource::PowerMessageWindow::~PowerMessageWindow() {
}
}
-void
-PowerMonitorDeviceSource::PowerMessageWindow::ProcessWmPowerBroadcastMessage(
- int event_id) {
- PowerMonitorSource::PowerEvent power_event;
- switch (event_id) {
- case PBT_APMPOWERSTATUSCHANGE: // The power status changed.
- power_event = PowerMonitorSource::POWER_STATE_EVENT;
- break;
- case PBT_APMRESUMEAUTOMATIC: // Resume from suspend.
- //case PBT_APMRESUMESUSPEND: // User-initiated resume from suspend.
- // We don't notify for this latter event
- // because if it occurs it is always sent as a
- // second event after PBT_APMRESUMEAUTOMATIC.
- power_event = PowerMonitorSource::RESUME_EVENT;
- break;
- case PBT_APMSUSPEND: // System has been suspended.
- power_event = PowerMonitorSource::SUSPEND_EVENT;
- break;
- default:
- return;
-
- // Other Power Events:
- // PBT_APMBATTERYLOW - removed in Vista.
- // PBT_APMOEMEVENT - removed in Vista.
- // PBT_APMQUERYSUSPEND - removed in Vista.
- // PBT_APMQUERYSUSPENDFAILED - removed in Vista.
- // PBT_APMRESUMECRITICAL - removed in Vista.
- // PBT_POWERSETTINGCHANGE - user changed the power settings.
- }
-
- ProcessPowerEventHelper(power_event);
-}
-
-LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProc(
+// static
+LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk(
HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam) {
switch (message) {
- case WM_POWERBROADCAST: {
- DWORD power_event = static_cast<DWORD>(message);
- ProcessWmPowerBroadcastMessage(power_event);
+ case WM_POWERBROADCAST:
+ ProcessWmPowerBroadcastMessage(wparam);
return TRUE;
- }
default:
- break;
+ return ::DefWindowProc(hwnd, message, wparam, lparam);
}
- return ::DefWindowProc(hwnd, message, wparam, lparam);
-}
-
-// static
-LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk(
- HWND hwnd,
- UINT message,
- WPARAM wparam,
- LPARAM lparam) {
- PowerMonitorDeviceSource::PowerMessageWindow* message_hwnd =
- reinterpret_cast<PowerMonitorDeviceSource::PowerMessageWindow*>(
- GetWindowLongPtr(hwnd, GWLP_USERDATA));
- if (message_hwnd)
- return message_hwnd->WndProc(hwnd, message, wparam, lparam);
- return ::DefWindowProc(hwnd, message, wparam, lparam);
}
} // namespace base
diff --git a/chromium/base/prefs/json_pref_store.cc b/chromium/base/prefs/json_pref_store.cc
index e230407043a..ad97b8459cb 100644
--- a/chromium/base/prefs/json_pref_store.cc
+++ b/chromium/base/prefs/json_pref_store.cc
@@ -217,14 +217,10 @@ void JsonPrefStore::SetValueSilently(const std::string& key,
}
void JsonPrefStore::RemoveValue(const std::string& key) {
- if (prefs_->Remove(key, NULL))
+ if (prefs_->RemovePath(key, NULL))
ReportValueChanged(key);
}
-void JsonPrefStore::MarkNeedsEmptyValue(const std::string& key) {
- keys_need_empty_value_.insert(key);
-}
-
bool JsonPrefStore::ReadOnly() const {
return read_only_;
}
@@ -324,35 +320,7 @@ JsonPrefStore::~JsonPrefStore() {
}
bool JsonPrefStore::SerializeData(std::string* output) {
- // TODO(tc): Do we want to prune webkit preferences that match the default
- // value?
JSONStringValueSerializer serializer(output);
serializer.set_pretty_print(true);
- scoped_ptr<base::DictionaryValue> copy(
- prefs_->DeepCopyWithoutEmptyChildren());
-
- // Iterates |keys_need_empty_value_| and if the key exists in |prefs_|,
- // ensure its empty ListValue or DictonaryValue is preserved.
- for (std::set<std::string>::const_iterator
- it = keys_need_empty_value_.begin();
- it != keys_need_empty_value_.end();
- ++it) {
- const std::string& key = *it;
-
- base::Value* value = NULL;
- if (!prefs_->Get(key, &value))
- continue;
-
- if (value->IsType(base::Value::TYPE_LIST)) {
- const base::ListValue* list = NULL;
- if (value->GetAsList(&list) && list->empty())
- copy->Set(key, new base::ListValue);
- } else if (value->IsType(base::Value::TYPE_DICTIONARY)) {
- const base::DictionaryValue* dict = NULL;
- if (value->GetAsDictionary(&dict) && dict->empty())
- copy->Set(key, new base::DictionaryValue);
- }
- }
-
- return serializer.Serialize(*(copy.get()));
+ return serializer.Serialize(*prefs_);
}
diff --git a/chromium/base/prefs/json_pref_store.h b/chromium/base/prefs/json_pref_store.h
index 9e6c1821834..21fc8f95ac9 100644
--- a/chromium/base/prefs/json_pref_store.h
+++ b/chromium/base/prefs/json_pref_store.h
@@ -21,8 +21,8 @@
namespace base {
class DictionaryValue;
class FilePath;
-class SequencedWorkerPool;
class SequencedTaskRunner;
+class SequencedWorkerPool;
class Value;
}
@@ -58,7 +58,6 @@ class BASE_PREFS_EXPORT JsonPrefStore
virtual void SetValueSilently(const std::string& key,
base::Value* value) OVERRIDE;
virtual void RemoveValue(const std::string& key) OVERRIDE;
- virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
virtual bool ReadOnly() const OVERRIDE;
virtual PrefReadError GetReadError() const OVERRIDE;
virtual PrefReadError ReadPrefs() OVERRIDE;
diff --git a/chromium/base/prefs/json_pref_store_unittest.cc b/chromium/base/prefs/json_pref_store_unittest.cc
index 34e1b8a9ede..a26afd71375 100644
--- a/chromium/base/prefs/json_pref_store_unittest.cc
+++ b/chromium/base/prefs/json_pref_store_unittest.cc
@@ -216,6 +216,33 @@ TEST_F(JsonPrefStoreTest, BasicAsync) {
pref_store.get(), input_file, data_dir_.AppendASCII("write.golden.json"));
}
+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());
+
+ // Set some keys with empty values.
+ pref_store->SetValue("list", new base::ListValue);
+ pref_store->SetValue("dict", new base::DictionaryValue);
+
+ // Write to file.
+ pref_store->CommitPendingWrite();
+ MessageLoop::current()->RunUntilIdle();
+
+ // Reload.
+ pref_store = new JsonPrefStore(pref_file, message_loop_.message_loop_proxy());
+ ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
+ ASSERT_FALSE(pref_store->ReadOnly());
+
+ // Check values.
+ const Value* result = NULL;
+ EXPECT_TRUE(pref_store->GetValue("list", &result));
+ EXPECT_TRUE(ListValue().Equals(result));
+ EXPECT_TRUE(pref_store->GetValue("dict", &result));
+ EXPECT_TRUE(DictionaryValue().Equals(result));
+}
+
// 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");
@@ -237,51 +264,4 @@ TEST_F(JsonPrefStoreTest, AsyncNonExistingFile) {
EXPECT_FALSE(pref_store->ReadOnly());
}
-TEST_F(JsonPrefStoreTest, NeedsEmptyValue) {
- base::FilePath pref_file = temp_dir_.path().AppendASCII("write.json");
-
- ASSERT_TRUE(base::CopyFile(
- data_dir_.AppendASCII("read.need_empty_value.json"),
- pref_file));
-
- // Test that the persistent value can be loaded.
- ASSERT_TRUE(PathExists(pref_file));
- scoped_refptr<JsonPrefStore> pref_store =
- new JsonPrefStore(pref_file, message_loop_.message_loop_proxy().get());
- ASSERT_EQ(PersistentPrefStore::PREF_READ_ERROR_NONE, pref_store->ReadPrefs());
- ASSERT_FALSE(pref_store->ReadOnly());
-
- // The JSON file looks like this:
- // {
- // "list": [ 1 ],
- // "list_needs_empty_value": [ 2 ],
- // "dict": {
- // "dummy": true,
- // },
- // "dict_needs_empty_value": {
- // "dummy": true,
- // },
- // }
-
- // Set flag to preserve empty values for the following keys.
- pref_store->MarkNeedsEmptyValue("list_needs_empty_value");
- pref_store->MarkNeedsEmptyValue("dict_needs_empty_value");
-
- // Set all keys to empty values.
- pref_store->SetValue("list", new base::ListValue);
- pref_store->SetValue("list_needs_empty_value", new base::ListValue);
- pref_store->SetValue("dict", new base::DictionaryValue);
- pref_store->SetValue("dict_needs_empty_value", new base::DictionaryValue);
-
- // Write to file.
- pref_store->CommitPendingWrite();
- RunLoop().RunUntilIdle();
-
- // Compare to expected output.
- base::FilePath golden_output_file =
- data_dir_.AppendASCII("write.golden.need_empty_value.json");
- ASSERT_TRUE(PathExists(golden_output_file));
- EXPECT_TRUE(TextContentsEqual(golden_output_file, pref_file));
-}
-
} // namespace base
diff --git a/chromium/base/prefs/overlay_user_pref_store.cc b/chromium/base/prefs/overlay_user_pref_store.cc
index 47668cca653..a708bb68363 100644
--- a/chromium/base/prefs/overlay_user_pref_store.cc
+++ b/chromium/base/prefs/overlay_user_pref_store.cc
@@ -93,11 +93,6 @@ void OverlayUserPrefStore::RemoveValue(const std::string& key) {
ReportValueChanged(key);
}
-void OverlayUserPrefStore::MarkNeedsEmptyValue(const std::string& key) {
- if (!ShallBeStoredInOverlay(key))
- underlay_->MarkNeedsEmptyValue(key);
-}
-
bool OverlayUserPrefStore::ReadOnly() const {
return false;
}
diff --git a/chromium/base/prefs/overlay_user_pref_store.h b/chromium/base/prefs/overlay_user_pref_store.h
index 1895ac0e4dd..c9993b94a0a 100644
--- a/chromium/base/prefs/overlay_user_pref_store.h
+++ b/chromium/base/prefs/overlay_user_pref_store.h
@@ -44,7 +44,6 @@ class BASE_PREFS_EXPORT OverlayUserPrefStore : public PersistentPrefStore,
virtual void SetValueSilently(const std::string& key,
base::Value* value) OVERRIDE;
virtual void RemoveValue(const std::string& key) OVERRIDE;
- virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
virtual bool ReadOnly() const OVERRIDE;
virtual PrefReadError GetReadError() const OVERRIDE;
virtual PrefReadError ReadPrefs() OVERRIDE;
diff --git a/chromium/base/prefs/persistent_pref_store.h b/chromium/base/prefs/persistent_pref_store.h
index 0baf02ac891..811ebff7014 100644
--- a/chromium/base/prefs/persistent_pref_store.h
+++ b/chromium/base/prefs/persistent_pref_store.h
@@ -63,10 +63,6 @@ class BASE_PREFS_EXPORT PersistentPrefStore : public PrefStore {
// Removes the value for |key|.
virtual void RemoveValue(const std::string& key) = 0;
- // Marks that the |key| with empty ListValue/DictionaryValue needs to be
- // persisted.
- virtual void MarkNeedsEmptyValue(const std::string& key) = 0;
-
// Whether the store is in a pseudo-read-only mode where changes are not
// actually persisted to disk. This happens in some cases when there are
// read errors during startup.
diff --git a/chromium/base/prefs/pref_registry.cc b/chromium/base/prefs/pref_registry.cc
index 31d788d2f38..9d6b05c8362 100644
--- a/chromium/base/prefs/pref_registry.cc
+++ b/chromium/base/prefs/pref_registry.cc
@@ -42,11 +42,6 @@ void PrefRegistry::SetDefaultPrefValue(const char* pref_name,
defaults_->ReplaceDefaultValue(pref_name, make_scoped_ptr(value));
}
-void PrefRegistry::SetRegistrationCallback(
- const RegistrationCallback& callback) {
- registration_callback_ = callback;
-}
-
void PrefRegistry::RegisterPreference(const char* path,
base::Value* default_value) {
base::Value::Type orig_type = default_value->GetType();
@@ -57,7 +52,4 @@ void PrefRegistry::RegisterPreference(const char* path,
"Trying to register a previously registered pref: " << path;
defaults_->SetDefaultValue(path, make_scoped_ptr(default_value));
-
- if (!registration_callback_.is_null())
- registration_callback_.Run(path, default_value);
}
diff --git a/chromium/base/prefs/pref_registry.h b/chromium/base/prefs/pref_registry.h
index 6c7eac9f595..896db3ffa62 100644
--- a/chromium/base/prefs/pref_registry.h
+++ b/chromium/base/prefs/pref_registry.h
@@ -5,7 +5,6 @@
#ifndef BASE_PREFS_PREF_REGISTRY_H_
#define BASE_PREFS_PREF_REGISTRY_H_
-#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/prefs/base_prefs_export.h"
#include "base/prefs/pref_value_map.h"
@@ -29,7 +28,6 @@ class PrefStore;
class BASE_PREFS_EXPORT PrefRegistry : public base::RefCounted<PrefRegistry> {
public:
typedef PrefValueMap::const_iterator const_iterator;
- typedef base::Callback<void(const char*, base::Value*)> RegistrationCallback;
PrefRegistry();
@@ -45,15 +43,6 @@ class BASE_PREFS_EXPORT PrefRegistry : public base::RefCounted<PrefRegistry> {
// |pref_name| must be a previously registered preference.
void SetDefaultPrefValue(const char* pref_name, base::Value* value);
- // Exactly one callback can be set for registration. The callback
- // will be invoked each time registration has been performed on this
- // object.
- //
- // Calling this method after a callback has already been set will
- // make the object forget the previous callback and use the new one
- // instead.
- void SetRegistrationCallback(const RegistrationCallback& callback);
-
protected:
friend class base::RefCounted<PrefRegistry>;
virtual ~PrefRegistry();
@@ -64,8 +53,6 @@ class BASE_PREFS_EXPORT PrefRegistry : public base::RefCounted<PrefRegistry> {
scoped_refptr<DefaultPrefStore> defaults_;
private:
- RegistrationCallback registration_callback_;
-
DISALLOW_COPY_AND_ASSIGN(PrefRegistry);
};
diff --git a/chromium/base/prefs/pref_service.cc b/chromium/base/prefs/pref_service.cc
index 046af915b33..576043b5489 100644
--- a/chromium/base/prefs/pref_service.cc
+++ b/chromium/base/prefs/pref_service.cc
@@ -53,20 +53,12 @@ PrefService::PrefService(
read_error_callback_(read_error_callback) {
pref_notifier_->SetPrefService(this);
- pref_registry_->SetRegistrationCallback(
- base::Bind(&PrefService::AddRegisteredPreference,
- base::Unretained(this)));
- AddInitialPreferences();
-
InitFromStorage(async);
}
PrefService::~PrefService() {
DCHECK(CalledOnValidThread());
- // Remove our callback, setting a NULL one.
- pref_registry_->SetRegistrationCallback(PrefRegistry::RegistrationCallback());
-
// Reset pointers so accesses after destruction reliably crash.
pref_value_store_.reset();
pref_registry_ = NULL;
@@ -87,11 +79,6 @@ void PrefService::InitFromStorage(bool async) {
}
}
-bool PrefService::ReloadPersistentPrefs() {
- return user_pref_store_->ReadPrefs() ==
- PersistentPrefStore::PREF_READ_ERROR_NONE;
-}
-
void PrefService::CommitPendingWrite() {
DCHECK(CalledOnValidThread());
user_pref_store_->CommitPendingWrite();
@@ -177,16 +164,29 @@ bool PrefService::HasPrefPath(const char* path) const {
return pref && !pref->IsDefaultValue();
}
-base::DictionaryValue* PrefService::GetPreferenceValues() const {
+scoped_ptr<base::DictionaryValue> PrefService::GetPreferenceValues() const {
DCHECK(CalledOnValidThread());
- base::DictionaryValue* out = new base::DictionaryValue;
+ scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
PrefRegistry::const_iterator i = pref_registry_->begin();
for (; i != pref_registry_->end(); ++i) {
const base::Value* value = GetPreferenceValue(i->first);
DCHECK(value);
out->Set(i->first, value->DeepCopy());
}
- return out;
+ return out.Pass();
+}
+
+scoped_ptr<base::DictionaryValue>
+PrefService::GetPreferenceValuesWithoutPathExpansion() const {
+ DCHECK(CalledOnValidThread());
+ scoped_ptr<base::DictionaryValue> out(new base::DictionaryValue);
+ PrefRegistry::const_iterator i = pref_registry_->begin();
+ for (; i != pref_registry_->end(); ++i) {
+ const base::Value* value = GetPreferenceValue(i->first);
+ DCHECK(value);
+ out->SetWithoutPathExpansion(i->first, value->DeepCopy());
+ }
+ return out.Pass();
}
const PrefService::Preference* PrefService::FindPreference(
@@ -320,42 +320,6 @@ PrefRegistry* PrefService::DeprecatedGetPrefRegistry() {
return pref_registry_.get();
}
-void PrefService::AddInitialPreferences() {
- for (PrefRegistry::const_iterator it = pref_registry_->begin();
- it != pref_registry_->end();
- ++it) {
- AddRegisteredPreference(it->first.c_str(), it->second);
- }
-}
-
-// TODO(joi): Once MarkNeedsEmptyValue is gone, we can probably
-// completely get rid of this method. There will be one difference in
-// semantics; currently all registered preferences are stored right
-// away in the prefs_map_, if we remove this they would be stored only
-// opportunistically.
-void PrefService::AddRegisteredPreference(const char* path,
- base::Value* default_value) {
- DCHECK(CalledOnValidThread());
-
- // For ListValue and DictionaryValue with non empty default, empty value
- // for |path| needs to be persisted in |user_pref_store_|. So that
- // non empty default is not used when user sets an empty ListValue or
- // DictionaryValue.
- bool needs_empty_value = false;
- base::Value::Type orig_type = default_value->GetType();
- if (orig_type == base::Value::TYPE_LIST) {
- const base::ListValue* list = NULL;
- if (default_value->GetAsList(&list) && !list->empty())
- needs_empty_value = true;
- } else if (orig_type == base::Value::TYPE_DICTIONARY) {
- const base::DictionaryValue* dict = NULL;
- if (default_value->GetAsDictionary(&dict) && !dict->empty())
- needs_empty_value = true;
- }
- if (needs_empty_value)
- user_pref_store_->MarkNeedsEmptyValue(path);
-}
-
void PrefService::ClearPref(const char* path) {
DCHECK(CalledOnValidThread());
diff --git a/chromium/base/prefs/pref_service.h b/chromium/base/prefs/pref_service.h
index 8af042f40b7..186433c1b62 100644
--- a/chromium/base/prefs/pref_service.h
+++ b/chromium/base/prefs/pref_service.h
@@ -138,7 +138,7 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe {
const PrefService* pref_service_;
};
- // You may wish to use PrefServiceBuilder or one of its subclasses
+ // You may wish to use PrefServiceFactory or one of its subclasses
// for simplified construction.
PrefService(
PrefNotifierImpl* pref_notifier,
@@ -150,11 +150,6 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe {
bool async);
virtual ~PrefService();
- // Reloads the data from file. This should only be called when the importer
- // is running during first run, and the main process may not change pref
- // values while the importer process is running. Returns true on success.
- bool ReloadPersistentPrefs();
-
// Lands pending writes to disk. This should only be used if we need to save
// immediately (basically, during shutdown).
void CommitPendingWrite();
@@ -234,7 +229,18 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// Returns a dictionary with effective preference values. The ownership
// is passed to the caller.
- base::DictionaryValue* GetPreferenceValues() const;
+ scoped_ptr<base::DictionaryValue> GetPreferenceValues() const;
+
+ // Returns a dictionary with effective preference values. Contrary to
+ // GetPreferenceValues(), the paths of registered preferences are not split on
+ // '.' characters. If a registered preference stores a dictionary, however,
+ // the hierarchical structure inside the preference will be preserved.
+ // For example, if "foo.bar" is a registered preference, the result could look
+ // like this:
+ // {"foo.bar": {"a": {"b": true}}}.
+ // The ownership is passed to the caller.
+ scoped_ptr<base::DictionaryValue> GetPreferenceValuesWithoutPathExpansion()
+ const;
bool ReadOnly() const;
@@ -252,20 +258,19 @@ class BASE_PREFS_EXPORT PrefService : public base::NonThreadSafe {
// Returns the PrefRegistry object for this service. You should not
// use this; the intent is for no registrations to take place after
// PrefService has been constructed.
+ //
+ // Instead of using this method, the recommended approach is to
+ // register all preferences for a class Xyz up front in a static
+ // Xyz::RegisterPrefs function, which gets invoked early in the
+ // application's start-up, before a PrefService is created.
+ //
+ // As an example, prefs registration in Chrome is triggered by the
+ // functions chrome::RegisterPrefs (for global preferences) and
+ // chrome::RegisterProfilePrefs (for user-specific preferences)
+ // implemented in chrome/browser/prefs/browser_prefs.cc.
PrefRegistry* DeprecatedGetPrefRegistry();
protected:
- // Adds the registered preferences from the PrefRegistry instance
- // passed to us at construction time.
- void AddInitialPreferences();
-
- // Updates local caches for a preference registered at |path|. The
- // |default_value| must not be NULL as it determines the preference
- // value's type. AddRegisteredPreference must not be called twice
- // for the same path.
- void AddRegisteredPreference(const char* path,
- base::Value* default_value);
-
// The PrefNotifier handles registering and notifying preference observers.
// It is created and owned by this PrefService. Subclasses may access it for
// unit testing.
diff --git a/chromium/base/prefs/pref_service_builder.cc b/chromium/base/prefs/pref_service_builder.cc
deleted file mode 100644
index 16b4565663b..00000000000
--- a/chromium/base/prefs/pref_service_builder.cc
+++ /dev/null
@@ -1,110 +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/prefs/pref_service_builder.h"
-
-#include "base/bind.h"
-#include "base/prefs/default_pref_store.h"
-#include "base/prefs/json_pref_store.h"
-#include "base/prefs/pref_notifier_impl.h"
-#include "base/prefs/pref_service.h"
-
-#include "base/prefs/pref_value_store.h"
-
-namespace {
-
-// Do-nothing default implementation.
-void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {
-}
-
-} // namespace
-
-PrefServiceBuilder::PrefServiceBuilder() {
- ResetDefaultState();
-}
-
-PrefServiceBuilder::~PrefServiceBuilder() {
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithManagedPrefs(PrefStore* store) {
- managed_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithSupervisedUserPrefs(
- PrefStore* store) {
- supervised_user_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithExtensionPrefs(PrefStore* store) {
- extension_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithCommandLinePrefs(PrefStore* store) {
- command_line_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithUserPrefs(
- PersistentPrefStore* store) {
- user_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithRecommendedPrefs(PrefStore* store) {
- recommended_prefs_ = store;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithReadErrorCallback(
- const base::Callback<void(PersistentPrefStore::PrefReadError)>&
- read_error_callback) {
- read_error_callback_ = read_error_callback;
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithUserFilePrefs(
- const base::FilePath& prefs_file,
- base::SequencedTaskRunner* task_runner) {
- user_prefs_ = new JsonPrefStore(prefs_file, task_runner);
- return *this;
-}
-
-PrefServiceBuilder& PrefServiceBuilder::WithAsync(bool async) {
- async_ = async;
- return *this;
-}
-
-PrefService* PrefServiceBuilder::Create(PrefRegistry* pref_registry) {
- PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
- PrefService* pref_service =
- new PrefService(pref_notifier,
- new PrefValueStore(managed_prefs_.get(),
- supervised_user_prefs_.get(),
- extension_prefs_.get(),
- command_line_prefs_.get(),
- user_prefs_.get(),
- recommended_prefs_.get(),
- pref_registry->defaults().get(),
- pref_notifier),
- user_prefs_.get(),
- pref_registry,
- read_error_callback_,
- async_);
- ResetDefaultState();
- return pref_service;
-}
-
-void PrefServiceBuilder::ResetDefaultState() {
- managed_prefs_ = NULL;
- supervised_user_prefs_ = NULL;
- extension_prefs_ = NULL;
- command_line_prefs_ = NULL;
- user_prefs_ = NULL;
- recommended_prefs_ = NULL;
- read_error_callback_ = base::Bind(&DoNothingHandleReadError);
- async_ = false;
-}
diff --git a/chromium/base/prefs/pref_service_builder.h b/chromium/base/prefs/pref_service_builder.h
deleted file mode 100644
index 7af43926233..00000000000
--- a/chromium/base/prefs/pref_service_builder.h
+++ /dev/null
@@ -1,74 +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_PREFS_PREF_SERVICE_BUILDER_H_
-#define BASE_PREFS_PREF_SERVICE_BUILDER_H_
-
-#include "base/basictypes.h"
-#include "base/callback.h"
-#include "base/memory/ref_counted.h"
-#include "base/prefs/base_prefs_export.h"
-#include "base/prefs/persistent_pref_store.h"
-#include "base/prefs/pref_registry.h"
-#include "base/prefs/pref_store.h"
-
-class PrefService;
-
-namespace base {
-class FilePath;
-class SequencedTaskRunner;
-}
-
-// A class that allows convenient building of PrefService.
-class BASE_PREFS_EXPORT PrefServiceBuilder {
- public:
- PrefServiceBuilder();
- virtual ~PrefServiceBuilder();
-
- // Functions for setting the various parameters of the PrefService to build.
- // These take ownership of the |store| parameter.
- PrefServiceBuilder& WithManagedPrefs(PrefStore* store);
- PrefServiceBuilder& WithSupervisedUserPrefs(PrefStore* store);
- PrefServiceBuilder& WithExtensionPrefs(PrefStore* store);
- PrefServiceBuilder& WithCommandLinePrefs(PrefStore* store);
- PrefServiceBuilder& WithUserPrefs(PersistentPrefStore* store);
- PrefServiceBuilder& WithRecommendedPrefs(PrefStore* store);
-
- // Sets up error callback for the PrefService. A do-nothing default
- // is provided if this is not called.
- PrefServiceBuilder& WithReadErrorCallback(
- const base::Callback<void(PersistentPrefStore::PrefReadError)>&
- read_error_callback);
-
- // Specifies to use an actual file-backed user pref store.
- PrefServiceBuilder& WithUserFilePrefs(
- const base::FilePath& prefs_file,
- base::SequencedTaskRunner* task_runner);
-
- PrefServiceBuilder& WithAsync(bool async);
-
- // Creates a PrefService object initialized with the parameters from
- // this builder.
- virtual PrefService* Create(PrefRegistry* registry);
-
- protected:
- virtual void ResetDefaultState();
-
- scoped_refptr<PrefStore> managed_prefs_;
- scoped_refptr<PrefStore> supervised_user_prefs_;
- scoped_refptr<PrefStore> extension_prefs_;
- scoped_refptr<PrefStore> command_line_prefs_;
- scoped_refptr<PersistentPrefStore> user_prefs_;
- scoped_refptr<PrefStore> recommended_prefs_;
-
- base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_;
-
- // Defaults to false.
- bool async_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(PrefServiceBuilder);
-};
-
-#endif // BASE_PREFS_PREF_SERVICE_BUILDER_H_
diff --git a/chromium/base/prefs/pref_service_factory.cc b/chromium/base/prefs/pref_service_factory.cc
new file mode 100644
index 00000000000..9c598530c1b
--- /dev/null
+++ b/chromium/base/prefs/pref_service_factory.cc
@@ -0,0 +1,63 @@
+// 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/prefs/pref_service_factory.h"
+
+#include "base/bind.h"
+#include "base/prefs/default_pref_store.h"
+#include "base/prefs/json_pref_store.h"
+#include "base/prefs/pref_notifier_impl.h"
+#include "base/prefs/pref_service.h"
+
+#include "base/prefs/pref_value_store.h"
+
+namespace base {
+
+namespace {
+
+// Do-nothing default implementation.
+void DoNothingHandleReadError(PersistentPrefStore::PrefReadError error) {
+}
+
+} // namespace
+
+PrefServiceFactory::PrefServiceFactory()
+ : managed_prefs_(NULL),
+ supervised_user_prefs_(NULL),
+ extension_prefs_(NULL),
+ command_line_prefs_(NULL),
+ user_prefs_(NULL),
+ recommended_prefs_(NULL),
+ read_error_callback_(base::Bind(&DoNothingHandleReadError)),
+ async_(false) {}
+
+PrefServiceFactory::~PrefServiceFactory() {}
+
+void PrefServiceFactory::SetUserPrefsFile(
+ const base::FilePath& prefs_file,
+ base::SequencedTaskRunner* task_runner) {
+ user_prefs_ = new JsonPrefStore(prefs_file, task_runner);
+}
+
+scoped_ptr<PrefService> PrefServiceFactory::Create(
+ PrefRegistry* pref_registry) {
+ PrefNotifierImpl* pref_notifier = new PrefNotifierImpl();
+ scoped_ptr<PrefService> pref_service(
+ new PrefService(pref_notifier,
+ new PrefValueStore(managed_prefs_.get(),
+ supervised_user_prefs_.get(),
+ extension_prefs_.get(),
+ command_line_prefs_.get(),
+ user_prefs_.get(),
+ recommended_prefs_.get(),
+ pref_registry->defaults().get(),
+ pref_notifier),
+ user_prefs_.get(),
+ pref_registry,
+ read_error_callback_,
+ async_));
+ return pref_service.Pass();
+}
+
+} // namespace base
diff --git a/chromium/base/prefs/pref_service_factory.h b/chromium/base/prefs/pref_service_factory.h
new file mode 100644
index 00000000000..ca608c2237b
--- /dev/null
+++ b/chromium/base/prefs/pref_service_factory.h
@@ -0,0 +1,91 @@
+// 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_SERVICE_FACTORY_H_
+#define BASE_PREFS_PREF_SERVICE_FACTORY_H_
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/persistent_pref_store.h"
+#include "base/prefs/pref_registry.h"
+#include "base/prefs/pref_store.h"
+
+class PrefService;
+
+namespace base {
+
+class FilePath;
+class SequencedTaskRunner;
+
+// A class that allows convenient building of PrefService.
+class BASE_PREFS_EXPORT PrefServiceFactory {
+ public:
+ PrefServiceFactory();
+ virtual ~PrefServiceFactory();
+
+ // Functions for setting the various parameters of the PrefService to build.
+ void set_managed_prefs(const scoped_refptr<PrefStore>& managed_prefs) {
+ managed_prefs_ = managed_prefs;
+ }
+ void set_supervised_user_prefs(
+ const scoped_refptr<PrefStore>& supervised_user_prefs) {
+ supervised_user_prefs_ = supervised_user_prefs;
+ }
+ void set_extension_prefs(const scoped_refptr<PrefStore>& extension_prefs) {
+ extension_prefs_ = extension_prefs;
+ }
+ void set_command_line_prefs(
+ const scoped_refptr<PrefStore>& command_line_prefs) {
+ command_line_prefs_ = command_line_prefs;
+ }
+ void set_user_prefs(const scoped_refptr<PersistentPrefStore>& user_prefs) {
+ user_prefs_ = user_prefs;
+ }
+ void set_recommended_prefs(
+ const scoped_refptr<PrefStore>& recommended_prefs) {
+ recommended_prefs_ = recommended_prefs;
+ }
+
+ // Sets up error callback for the PrefService. A do-nothing default
+ // is provided if this is not called.
+ void set_read_error_callback(
+ const base::Callback<void(PersistentPrefStore::PrefReadError)>&
+ read_error_callback) {
+ read_error_callback_ = read_error_callback;
+ }
+
+ // Specifies to use an actual file-backed user pref store.
+ void SetUserPrefsFile(const base::FilePath& prefs_file,
+ base::SequencedTaskRunner* task_runner);
+
+ void set_async(bool async) {
+ async_ = async;
+ }
+
+ // Creates a PrefService object initialized with the parameters from
+ // this factory.
+ scoped_ptr<PrefService> Create(PrefRegistry* registry);
+
+ protected:
+ scoped_refptr<PrefStore> managed_prefs_;
+ scoped_refptr<PrefStore> supervised_user_prefs_;
+ scoped_refptr<PrefStore> extension_prefs_;
+ scoped_refptr<PrefStore> command_line_prefs_;
+ scoped_refptr<PersistentPrefStore> user_prefs_;
+ scoped_refptr<PrefStore> recommended_prefs_;
+
+ base::Callback<void(PersistentPrefStore::PrefReadError)> read_error_callback_;
+
+ // Defaults to false.
+ bool async_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PrefServiceFactory);
+};
+
+} // namespace base
+
+#endif // BASE_PREFS_PREF_SERVICE_FACTORY_H_
diff --git a/chromium/base/prefs/scoped_user_pref_update.cc b/chromium/base/prefs/scoped_user_pref_update.cc
new file mode 100644
index 00000000000..c86b1634b52
--- /dev/null
+++ b/chromium/base/prefs/scoped_user_pref_update.cc
@@ -0,0 +1,36 @@
+// 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/prefs/scoped_user_pref_update.h"
+
+#include "base/logging.h"
+#include "base/prefs/pref_notifier.h"
+#include "base/prefs/pref_service.h"
+
+namespace subtle {
+
+ScopedUserPrefUpdateBase::ScopedUserPrefUpdateBase(PrefService* service,
+ const char* path)
+ : service_(service),
+ path_(path),
+ value_(NULL) {}
+
+ScopedUserPrefUpdateBase::~ScopedUserPrefUpdateBase() {
+ Notify();
+}
+
+Value* ScopedUserPrefUpdateBase::GetValueOfType(base::Value::Type type) {
+ if (!value_)
+ value_ = service_->GetMutableUserPref(path_.c_str(), type);
+ return value_;
+}
+
+void ScopedUserPrefUpdateBase::Notify() {
+ if (value_) {
+ service_->ReportUserPrefChanged(path_);
+ value_ = NULL;
+ }
+}
+
+} // namespace subtle
diff --git a/chromium/base/prefs/scoped_user_pref_update.h b/chromium/base/prefs/scoped_user_pref_update.h
new file mode 100644
index 00000000000..82d6739dd32
--- /dev/null
+++ b/chromium/base/prefs/scoped_user_pref_update.h
@@ -0,0 +1,108 @@
+// 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.
+//
+// A helper class that assists preferences in firing notifications when lists
+// or dictionaries are changed.
+
+#ifndef BASE_PREFS_SCOPED_USER_PREF_UPDATE_H_
+#define BASE_PREFS_SCOPED_USER_PREF_UPDATE_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/prefs/base_prefs_export.h"
+#include "base/prefs/pref_service.h"
+#include "base/threading/non_thread_safe.h"
+#include "base/values.h"
+
+class PrefService;
+
+namespace base {
+class DictionaryValue;
+class ListValue;
+}
+
+namespace subtle {
+
+// Base class for ScopedUserPrefUpdateTemplate that contains the parts
+// that do not depend on ScopedUserPrefUpdateTemplate's template parameter.
+//
+// We need this base class mostly for making it a friend of PrefService
+// and getting access to PrefService::GetMutableUserPref and
+// PrefService::ReportUserPrefChanged.
+class BASE_PREFS_EXPORT ScopedUserPrefUpdateBase : public base::NonThreadSafe {
+ protected:
+ ScopedUserPrefUpdateBase(PrefService* service, const char* path);
+
+ // Calls Notify().
+ ~ScopedUserPrefUpdateBase();
+
+ // Sets |value_| to |service_|->GetMutableUserPref and returns it.
+ base::Value* GetValueOfType(base::Value::Type type);
+
+ private:
+ // If |value_| is not null, triggers a notification of PrefObservers and
+ // resets |value_|.
+ void Notify();
+
+ // Weak pointer.
+ PrefService* service_;
+ // Path of the preference being updated.
+ std::string path_;
+ // Cache of value from user pref store (set between Get() and Notify() calls).
+ base::Value* value_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdateBase);
+};
+
+} // namespace subtle
+
+// Class to support modifications to DictionaryValues and ListValues while
+// guaranteeing that PrefObservers are notified of changed values.
+//
+// This class may only be used on the UI thread as it requires access to the
+// PrefService.
+template <typename T, base::Value::Type type_enum_value>
+class ScopedUserPrefUpdate : public subtle::ScopedUserPrefUpdateBase {
+ public:
+ ScopedUserPrefUpdate(PrefService* service, const char* path)
+ : ScopedUserPrefUpdateBase(service, path) {}
+
+ // Triggers an update notification if Get() was called.
+ virtual ~ScopedUserPrefUpdate() {}
+
+ // Returns a mutable |T| instance that
+ // - is already in the user pref store, or
+ // - is (silently) created and written to the user pref store if none existed
+ // before.
+ //
+ // Calling Get() implies that an update notification is necessary at
+ // destruction time.
+ //
+ // The ownership of the return value remains with the user pref store.
+ // Virtual so it can be overriden in subclasses that transform the value
+ // before returning it (for example to return a subelement of a dictionary).
+ virtual T* Get() {
+ return static_cast<T*>(GetValueOfType(type_enum_value));
+ }
+
+ T& operator*() {
+ return *Get();
+ }
+
+ T* operator->() {
+ return Get();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedUserPrefUpdate);
+};
+
+typedef ScopedUserPrefUpdate<base::DictionaryValue,
+ base::Value::TYPE_DICTIONARY>
+ DictionaryPrefUpdate;
+typedef ScopedUserPrefUpdate<base::ListValue, base::Value::TYPE_LIST>
+ ListPrefUpdate;
+
+#endif // BASE_PREFS_SCOPED_USER_PREF_UPDATE_H_
diff --git a/chromium/base/prefs/scoped_user_pref_update_unittest.cc b/chromium/base/prefs/scoped_user_pref_update_unittest.cc
new file mode 100644
index 00000000000..505526c07cf
--- /dev/null
+++ b/chromium/base/prefs/scoped_user_pref_update_unittest.cc
@@ -0,0 +1,81 @@
+// 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/prefs/mock_pref_change_callback.h"
+#include "base/prefs/pref_change_registrar.h"
+#include "base/prefs/pref_registry_simple.h"
+#include "base/prefs/scoped_user_pref_update.h"
+#include "base/prefs/testing_pref_service.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::_;
+using testing::Mock;
+
+class ScopedUserPrefUpdateTest : public testing::Test {
+ public:
+ ScopedUserPrefUpdateTest() : observer_(&prefs_) {}
+ virtual ~ScopedUserPrefUpdateTest() {}
+
+ protected:
+ virtual void SetUp() {
+ prefs_.registry()->RegisterDictionaryPref(kPref);
+ registrar_.Init(&prefs_);
+ registrar_.Add(kPref, observer_.GetCallback());
+ }
+
+ static const char kPref[];
+ static const char kKey[];
+ static const char kValue[];
+
+ TestingPrefServiceSimple prefs_;
+ MockPrefChangeCallback observer_;
+ PrefChangeRegistrar registrar_;
+};
+
+const char ScopedUserPrefUpdateTest::kPref[] = "name";
+const char ScopedUserPrefUpdateTest::kKey[] = "key";
+const char ScopedUserPrefUpdateTest::kValue[] = "value";
+
+TEST_F(ScopedUserPrefUpdateTest, RegularUse) {
+ // Dictionary that will be expected to be set at the end.
+ DictionaryValue expected_dictionary;
+ expected_dictionary.SetString(kKey, kValue);
+
+ {
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ DictionaryPrefUpdate update(&prefs_, kPref);
+ DictionaryValue* value = update.Get();
+ ASSERT_TRUE(value);
+ value->SetString(kKey, kValue);
+
+ // The dictionary was created for us but the creation should have happened
+ // silently without notifications.
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ // Modifications happen online and are instantly visible, though.
+ const DictionaryValue* current_value = prefs_.GetDictionary(kPref);
+ ASSERT_TRUE(current_value);
+ EXPECT_TRUE(expected_dictionary.Equals(current_value));
+
+ // Now we are leaving the scope of the update so we should be notified.
+ observer_.Expect(kPref, &expected_dictionary);
+ }
+ Mock::VerifyAndClearExpectations(&observer_);
+
+ const 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);
+ EXPECT_CALL(observer_, OnPreferenceChanged(_)).Times(0);
+ {
+ DictionaryPrefUpdate update(&prefs_, kPref);
+ }
+ const DictionaryValue* new_value = prefs_.GetDictionary(kPref);
+ EXPECT_EQ(old_value, new_value);
+ Mock::VerifyAndClearExpectations(&observer_);
+}
diff --git a/chromium/base/prefs/testing_pref_service.h b/chromium/base/prefs/testing_pref_service.h
index 1af4ba6d1e1..a9ab937429b 100644
--- a/chromium/base/prefs/testing_pref_service.h
+++ b/chromium/base/prefs/testing_pref_service.h
@@ -90,7 +90,7 @@ class TestingPrefServiceSimple
// an existing TestingPrefServiceSimple instance. On a production
// PrefService you would do all registrations before constructing
// it, passing it a PrefRegistry via its constructor (or via
- // e.g. PrefServiceBuilder).
+ // e.g. PrefServiceFactory).
PrefRegistrySimple* registry();
private:
diff --git a/chromium/base/prefs/testing_pref_store.cc b/chromium/base/prefs/testing_pref_store.cc
index b4209695a6b..2f429c9a1a9 100644
--- a/chromium/base/prefs/testing_pref_store.cc
+++ b/chromium/base/prefs/testing_pref_store.cc
@@ -53,9 +53,6 @@ void TestingPrefStore::RemoveValue(const std::string& key) {
NotifyPrefValueChanged(key);
}
-void TestingPrefStore::MarkNeedsEmptyValue(const std::string& key) {
-}
-
bool TestingPrefStore::ReadOnly() const {
return read_only_;
}
diff --git a/chromium/base/prefs/testing_pref_store.h b/chromium/base/prefs/testing_pref_store.h
index 08d7125e241..c6a2b8336f0 100644
--- a/chromium/base/prefs/testing_pref_store.h
+++ b/chromium/base/prefs/testing_pref_store.h
@@ -36,7 +36,6 @@ class TestingPrefStore : public PersistentPrefStore {
virtual void SetValueSilently(const std::string& key,
base::Value* value) OVERRIDE;
virtual void RemoveValue(const std::string& key) OVERRIDE;
- virtual void MarkNeedsEmptyValue(const std::string& key) OVERRIDE;
virtual bool ReadOnly() const OVERRIDE;
virtual PrefReadError GetReadError() const OVERRIDE;
virtual PersistentPrefStore::PrefReadError ReadPrefs() OVERRIDE;
diff --git a/chromium/base/process/internal_linux.h b/chromium/base/process/internal_linux.h
index a10cee36e56..0cacd3f3a84 100644
--- a/chromium/base/process/internal_linux.h
+++ b/chromium/base/process/internal_linux.h
@@ -8,6 +8,8 @@
#ifndef BASE_PROCESS_LINUX_INTERNAL_H_
#define BASE_PROCESS_LINUX_INTERNAL_H_
+#include <unistd.h>
+
#include "base/files/file_path.h"
namespace base {
diff --git a/chromium/base/process/kill.h b/chromium/base/process/kill.h
index c828c718def..de72d7a8dca 100644
--- a/chromium/base/process/kill.h
+++ b/chromium/base/process/kill.h
@@ -25,6 +25,13 @@ enum TerminationStatus {
TERMINATION_STATUS_PROCESS_WAS_KILLED, // e.g. SIGKILL or task manager kill
TERMINATION_STATUS_PROCESS_CRASHED, // e.g. Segmentation fault
TERMINATION_STATUS_STILL_RUNNING, // child hasn't exited yet
+#if defined(OS_ANDROID)
+ // On Android processes are spawned from the system Zygote and we do not get
+ // the termination status. We can't know if the termination was a crash or an
+ // oom kill for sure, but we can use status of the strong process bindings as
+ // a hint.
+ TERMINATION_STATUS_OOM_PROTECTED, // child was protected from oom kill
+#endif
TERMINATION_STATUS_MAX_ENUM
};
diff --git a/chromium/base/process/launch.cc b/chromium/base/process/launch.cc
index 1329a5af23e..0c9f3a88fef 100644
--- a/chromium/base/process/launch.cc
+++ b/chromium/base/process/launch.cc
@@ -10,6 +10,7 @@ LaunchOptions::LaunchOptions()
: wait(false),
#if defined(OS_WIN)
start_hidden(false),
+ handles_to_inherit(NULL),
inherit_handles(false),
as_user(NULL),
empty_desktop_name(false),
diff --git a/chromium/base/process/launch.h b/chromium/base/process/launch.h
index ac2df5eee5f..336bfba16e9 100644
--- a/chromium/base/process/launch.h
+++ b/chromium/base/process/launch.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2013 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.
@@ -16,17 +16,23 @@
#include "base/basictypes.h"
#include "base/environment.h"
#include "base/process/process_handle.h"
+#include "base/strings/string_piece.h"
#if defined(OS_POSIX)
#include "base/posix/file_descriptor_shuffle.h"
#elif defined(OS_WIN)
#include <windows.h>
+#include "base/win/scoped_handle.h"
#endif
class CommandLine;
namespace base {
+#if defined(OS_WIN)
+typedef std::vector<HANDLE> HandlesToInheritVector;
+#endif
+// TODO(viettrungluu): Only define this on POSIX?
typedef std::vector<std::pair<int, int> > FileHandleMappingVector;
// Options for launching a subprocess that are passed to LaunchProcess().
@@ -41,13 +47,19 @@ struct BASE_EXPORT LaunchOptions {
#if defined(OS_WIN)
bool start_hidden;
+ // If non-null, inherit exactly the list of handles in this vector (these
+ // handles must be inheritable). This is only supported on Vista and higher.
+ HandlesToInheritVector* handles_to_inherit;
+
// If true, the new process inherits handles from the parent. In production
// code this flag should be used only when running short-lived, trusted
// binaries, because open handles from other libraries and subsystems will
// leak to the child process, causing errors such as open socket hangs.
+ // Note: If |handles_to_inherit| is non-null, this flag is ignored and only
+ // those handles will be inherited (on Vista and higher).
bool inherit_handles;
- // If non-NULL, runs as if the user represented by the token had launched it.
+ // If non-null, runs as if the user represented by the token had launched it.
// Whether the application is visible on the interactive desktop depends on
// the token belonging to an interactive logon session.
//
@@ -59,7 +71,7 @@ struct BASE_EXPORT LaunchOptions {
// If true, use an empty string for the desktop name.
bool empty_desktop_name;
- // If non-NULL, launches the application in that job object. The process will
+ // If non-null, launches the application in that job object. The process will
// be terminated immediately and LaunchProcess() will fail if assignment to
// the job object fails.
HANDLE job_handle;
@@ -81,7 +93,7 @@ struct BASE_EXPORT LaunchOptions {
// the same environment. See AlterEnvironment().
EnvironmentMap environ;
- // If non-NULL, remap file descriptors according to the mapping of
+ // If non-null, remap file descriptors according to the mapping of
// src fd->dest fd to propagate FDs into the child process.
// This pointer is owned by the caller and must live through the
// call to LaunchProcess().
@@ -116,7 +128,7 @@ struct BASE_EXPORT LaunchOptions {
//
// Returns true upon success.
//
-// Upon success, if |process_handle| is non-NULL, it will be filled in with the
+// Upon success, if |process_handle| is non-null, it will be filled in with the
// handle of the launched process. NOTE: In this case, the caller is
// responsible for closing the handle so that it doesn't leak!
// Otherwise, the process handle will be implicitly closed.
@@ -146,7 +158,7 @@ BASE_EXPORT bool LaunchProcess(const CommandLine& cmdline,
// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
BASE_EXPORT bool LaunchProcess(const string16& cmdline,
const LaunchOptions& options,
- ProcessHandle* process_handle);
+ win::ScopedHandle* process_handle);
#elif defined(OS_POSIX)
// A POSIX-specific version of LaunchProcess that takes an argv array
@@ -179,6 +191,13 @@ BASE_EXPORT void RouteStdioToConsole();
// indicating success).
BASE_EXPORT bool GetAppOutput(const CommandLine& cl, std::string* output);
+#if defined(OS_WIN)
+// A Windows-specific version of GetAppOutput that takes a command line string
+// instead of a CommandLine object. Useful for situations where you need to
+// control the command line arguments directly.
+BASE_EXPORT bool GetAppOutput(const StringPiece16& cl, std::string* output);
+#endif
+
#if defined(OS_POSIX)
// A POSIX-specific version of GetAppOutput that takes an argv array
// instead of a CommandLine. Useful for situations where you need to
diff --git a/chromium/base/process/launch_posix.cc b/chromium/base/process/launch_posix.cc
index de6286da26b..8dc8f9e215d 100644
--- a/chromium/base/process/launch_posix.cc
+++ b/chromium/base/process/launch_posix.cc
@@ -230,7 +230,7 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
// Since we're just trying to close anything we can find,
// ignore any error return values of close().
- ignore_result(HANDLE_EINTR(close(fd)));
+ close(fd);
}
return;
}
@@ -264,7 +264,7 @@ void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) {
// these FDs are >= |max_fds|, so we can check against that here
// before closing. See https://bugs.kde.org/show_bug.cgi?id=191758
if (fd < static_cast<int>(max_fds)) {
- int ret = HANDLE_EINTR(close(fd));
+ int ret = IGNORE_EINTR(close(fd));
DPCHECK(ret == 0);
}
}
diff --git a/chromium/base/process/launch_win.cc b/chromium/base/process/launch_win.cc
index da913efba74..0c831cf7b4a 100644
--- a/chromium/base/process/launch_win.cc
+++ b/chromium/base/process/launch_win.cc
@@ -11,6 +11,7 @@
#include <psapi.h>
#include <ios>
+#include <limits>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -25,6 +26,7 @@
#include "base/win/object_watcher.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
+#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
// userenv.dll is required for CreateEnvironmentBlock().
@@ -103,27 +105,62 @@ void RouteStdioToConsole() {
bool LaunchProcess(const string16& cmdline,
const LaunchOptions& options,
- ProcessHandle* process_handle) {
- STARTUPINFO startup_info = {};
- startup_info.cb = sizeof(startup_info);
+ win::ScopedHandle* process_handle) {
+ win::StartupInformation startup_info_wrapper;
+ STARTUPINFO* startup_info = startup_info_wrapper.startup_info();
+
+ bool inherit_handles = options.inherit_handles;
+ DWORD flags = 0;
+ if (options.handles_to_inherit) {
+ if (options.handles_to_inherit->empty()) {
+ inherit_handles = false;
+ } else {
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ DLOG(ERROR) << "Specifying handles to inherit requires Vista or later.";
+ return false;
+ }
+
+ if (options.handles_to_inherit->size() >
+ std::numeric_limits<DWORD>::max() / sizeof(HANDLE)) {
+ DLOG(ERROR) << "Too many handles to inherit.";
+ return false;
+ }
+
+ if (!startup_info_wrapper.InitializeProcThreadAttributeList(1)) {
+ DPLOG(ERROR);
+ return false;
+ }
+
+ if (!startup_info_wrapper.UpdateProcThreadAttribute(
+ PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+ const_cast<HANDLE*>(&options.handles_to_inherit->at(0)),
+ static_cast<DWORD>(options.handles_to_inherit->size() *
+ sizeof(HANDLE)))) {
+ DPLOG(ERROR);
+ return false;
+ }
+
+ inherit_handles = true;
+ flags |= EXTENDED_STARTUPINFO_PRESENT;
+ }
+ }
+
if (options.empty_desktop_name)
- startup_info.lpDesktop = L"";
- startup_info.dwFlags = STARTF_USESHOWWINDOW;
- startup_info.wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
+ startup_info->lpDesktop = L"";
+ startup_info->dwFlags = STARTF_USESHOWWINDOW;
+ startup_info->wShowWindow = options.start_hidden ? SW_HIDE : SW_SHOW;
if (options.stdin_handle || options.stdout_handle || options.stderr_handle) {
- DCHECK(options.inherit_handles);
+ DCHECK(inherit_handles);
DCHECK(options.stdin_handle);
DCHECK(options.stdout_handle);
DCHECK(options.stderr_handle);
- startup_info.dwFlags |= STARTF_USESTDHANDLES;
- startup_info.hStdInput = options.stdin_handle;
- startup_info.hStdOutput = options.stdout_handle;
- startup_info.hStdError = options.stderr_handle;
+ startup_info->dwFlags |= STARTF_USESTDHANDLES;
+ startup_info->hStdInput = options.stdin_handle;
+ startup_info->hStdOutput = options.stdout_handle;
+ startup_info->hStdError = options.stderr_handle;
}
- DWORD flags = 0;
-
if (options.job_handle) {
flags |= CREATE_SUSPENDED;
@@ -136,7 +173,7 @@ bool LaunchProcess(const string16& cmdline,
if (options.force_breakaway_from_job_)
flags |= CREATE_BREAKAWAY_FROM_JOB;
- base::win::ScopedProcessInformation process_info;
+ PROCESS_INFORMATION temp_process_info = {};
if (options.as_user) {
flags |= CREATE_UNICODE_ENVIRONMENT;
@@ -150,9 +187,9 @@ bool LaunchProcess(const string16& cmdline,
BOOL launched =
CreateProcessAsUser(options.as_user, NULL,
const_cast<wchar_t*>(cmdline.c_str()),
- NULL, NULL, options.inherit_handles, flags,
- enviroment_block, NULL, &startup_info,
- process_info.Receive());
+ NULL, NULL, inherit_handles, flags,
+ enviroment_block, NULL, startup_info,
+ &temp_process_info);
DestroyEnvironmentBlock(enviroment_block);
if (!launched) {
DPLOG(ERROR);
@@ -161,12 +198,13 @@ bool LaunchProcess(const string16& cmdline,
} else {
if (!CreateProcess(NULL,
const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
- options.inherit_handles, flags, NULL, NULL,
- &startup_info, process_info.Receive())) {
+ inherit_handles, flags, NULL, NULL,
+ startup_info, &temp_process_info)) {
DPLOG(ERROR);
return false;
}
}
+ base::win::ScopedProcessInformation process_info(temp_process_info);
if (options.job_handle) {
if (0 == AssignProcessToJobObject(options.job_handle,
@@ -184,7 +222,7 @@ bool LaunchProcess(const string16& cmdline,
// If the caller wants the process handle, we won't close it.
if (process_handle)
- *process_handle = process_info.TakeProcessHandle();
+ process_handle->Set(process_info.TakeProcessHandle());
return true;
}
@@ -192,7 +230,13 @@ bool LaunchProcess(const string16& cmdline,
bool LaunchProcess(const CommandLine& cmdline,
const LaunchOptions& options,
ProcessHandle* process_handle) {
- return LaunchProcess(cmdline.GetCommandLineString(), options, process_handle);
+ if (!process_handle)
+ return LaunchProcess(cmdline.GetCommandLineString(), options, NULL);
+
+ win::ScopedHandle process;
+ bool rv = LaunchProcess(cmdline.GetCommandLineString(), options, &process);
+ *process_handle = process.Take();
+ return rv;
}
bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
@@ -206,6 +250,10 @@ bool SetJobObjectLimitFlags(HANDLE job_object, DWORD limit_flags) {
}
bool GetAppOutput(const CommandLine& cl, std::string* output) {
+ return GetAppOutput(cl.GetCommandLineString(), output);
+}
+
+bool GetAppOutput(const StringPiece16& cl, std::string* output) {
HANDLE out_read = NULL;
HANDLE out_write = NULL;
@@ -231,10 +279,10 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
return false;
}
- FilePath::StringType writable_command_line_string(cl.GetCommandLineString());
+ FilePath::StringType writable_command_line_string;
+ writable_command_line_string.assign(cl.data(), cl.size());
- base::win::ScopedProcessInformation proc_info;
- STARTUPINFO start_info = { 0 };
+ STARTUPINFO start_info = {};
start_info.cb = sizeof(STARTUPINFO);
start_info.hStdOutput = out_write;
@@ -244,14 +292,16 @@ bool GetAppOutput(const CommandLine& cl, std::string* output) {
start_info.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
+ PROCESS_INFORMATION temp_process_info = {};
if (!CreateProcess(NULL,
&writable_command_line_string[0],
NULL, NULL,
TRUE, // Handles are inherited.
- 0, NULL, NULL, &start_info, proc_info.Receive())) {
+ 0, NULL, NULL, &start_info, &temp_process_info)) {
NOTREACHED() << "Failed to start process";
return false;
}
+ base::win::ScopedProcessInformation proc_info(temp_process_info);
// Close our writing end of pipe now. Otherwise later read would not be able
// to detect end of child's output.
diff --git a/chromium/base/process/memory.h b/chromium/base/process/memory.h
index de79477e95d..e6696cb8a70 100644
--- a/chromium/base/process/memory.h
+++ b/chromium/base/process/memory.h
@@ -62,6 +62,7 @@ BASE_EXPORT bool AdjustOOMScore(ProcessId process, int score);
// 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)
} // namespace base
diff --git a/chromium/base/process/memory_linux.cc b/chromium/base/process/memory_linux.cc
index f81429b2ac0..6bed68bd832 100644
--- a/chromium/base/process/memory_linux.cc
+++ b/chromium/base/process/memory_linux.cc
@@ -18,6 +18,7 @@ size_t g_oom_size = 0U;
namespace {
+#if !defined(OS_ANDROID)
void OnNoMemorySize(size_t size) {
g_oom_size = size;
@@ -29,6 +30,7 @@ void OnNoMemorySize(size_t size) {
void OnNoMemory() {
OnNoMemorySize(0);
}
+#endif // !defined(OS_ANDROID)
} // namespace
diff --git a/chromium/base/process/memory_mac.mm b/chromium/base/process/memory_mac.mm
index dd30e704c8f..3e281cd8e3c 100644
--- a/chromium/base/process/memory_mac.mm
+++ b/chromium/base/process/memory_mac.mm
@@ -108,7 +108,7 @@ class ThreadLocalBooleanAutoReset {
};
base::LazyInstance<ThreadLocalBoolean>::Leaky
- g_unchecked_malloc = LAZY_INSTANCE_INITIALIZER;
+ g_unchecked_alloc = LAZY_INSTANCE_INITIALIZER;
// NOTE(shess): This is called when the malloc library noticed that the heap
// is fubar. Avoid calls which will re-enter the malloc library.
@@ -117,10 +117,23 @@ void CrMallocErrorBreak() {
// Out of memory is certainly not heap corruption, and not necessarily
// something for which the process should be terminated. Leave that decision
- // to the OOM killer. The EBADF case comes up because the malloc library
- // attempts to log to ASL (syslog) before calling this code, which fails
- // accessing a Unix-domain socket because of sandboxing.
- if (errno == ENOMEM || (errno == EBADF && g_unchecked_malloc.Get().Get()))
+ // to the OOM killer.
+ if (errno == ENOMEM)
+ return;
+
+ // The malloc library attempts to log to ASL (syslog) before calling this
+ // code, which fails accessing a Unix-domain socket when sandboxed. The
+ // failed socket results in writing to a -1 fd, leaving EBADF in errno. If
+ // UncheckedMalloc() is on the stack, for large allocations (15k and up) only
+ // an OOM failure leads here. Smaller allocations could also arrive here due
+ // to freelist corruption, but there is no way to distinguish that from OOM at
+ // this point.
+ //
+ // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause
+ // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc
+ // source code at this time.
+ // <http://crbug.com/312234>
+ if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.Get().Get())
return;
// A unit test checks this error message, so it needs to be in release builds.
@@ -422,7 +435,7 @@ void oom_killer_new() {
// === Core Foundation CFAllocators ===
bool CanGetContextForCFAllocator() {
- return !base::mac::IsOSLaterThanMountainLion_DontCallThis();
+ return !base::mac::IsOSLaterThanMavericks_DontCallThis();
}
CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
@@ -431,7 +444,9 @@ CFAllocatorContext* ContextForCFAllocator(CFAllocatorRef allocator) {
const_cast<ChromeCFAllocatorLeopards*>(
reinterpret_cast<const ChromeCFAllocatorLeopards*>(allocator));
return &our_allocator->_context;
- } else if (base::mac::IsOSLion() || base::mac::IsOSMountainLion()) {
+ } else if (base::mac::IsOSLion() ||
+ base::mac::IsOSMountainLion() ||
+ base::mac::IsOSMavericks()) {
ChromeCFAllocatorLions* our_allocator =
const_cast<ChromeCFAllocatorLions*>(
reinterpret_cast<const ChromeCFAllocatorLions*>(allocator));
@@ -491,13 +506,24 @@ void* UncheckedMalloc(size_t size) {
if (g_old_malloc) {
#if ARCH_CPU_32_BITS
ScopedClearErrno clear_errno;
- ThreadLocalBooleanAutoReset flag(g_unchecked_malloc.Pointer(), true);
+ ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
#endif // ARCH_CPU_32_BITS
return g_old_malloc(malloc_default_zone(), size);
}
return malloc(size);
}
+void* UncheckedCalloc(size_t num_items, size_t size) {
+ 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);
+ }
+ return calloc(num_items, size);
+}
+
void EnableTerminationOnOutOfMemory() {
if (g_oom_killer_enabled)
return;
diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc
index a1f30526aa3..e5c759d5711 100644
--- a/chromium/base/process/memory_unittest.cc
+++ b/chromium/base/process/memory_unittest.cc
@@ -10,6 +10,7 @@
#include "base/compiler_specific.h"
#include "base/debug/alias.h"
+#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_WIN)
@@ -23,7 +24,6 @@
#include "base/process/memory_unittest_mac.h"
#endif
#if defined(OS_LINUX)
-#include <glib.h>
#include <malloc.h>
#endif
@@ -260,14 +260,13 @@ TEST_F(OutOfMemoryDeathTest, Memalign) {
}
TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
- // g_try_malloc is documented to return NULL on failure. (g_malloc is the
- // 'safe' default that crashes if allocation fails). However, since we have
- // hopefully overridden malloc, even g_try_malloc should fail. This tests
- // that the run-time symbol resolution is overriding malloc for shared
- // libraries as well as for our code.
+ // This tests that the run-time symbol resolution is overriding malloc for
+ // shared libraries (including libc itself) as well as for our code.
+ std::string format = base::StringPrintf("%%%zud", test_size_);
+ char *value = NULL;
ASSERT_DEATH({
SetUpInDeathAssert();
- value_ = g_try_malloc(test_size_);
+ EXPECT_EQ(-1, asprintf(&value, format.c_str(), 0));
}, "");
}
#endif // OS_LINUX
diff --git a/chromium/base/process/process_handle_linux.cc b/chromium/base/process/process_handle_linux.cc
index 91441f7b38c..0f7ccd348a8 100644
--- a/chromium/base/process/process_handle_linux.cc
+++ b/chromium/base/process/process_handle_linux.cc
@@ -20,7 +20,7 @@ ProcessId GetParentProcessId(ProcessHandle process) {
FilePath GetProcessExecutablePath(ProcessHandle process) {
FilePath stat_file = internal::GetProcPidDir(process).Append("exe");
FilePath exe_name;
- if (!file_util::ReadSymbolicLink(stat_file, &exe_name)) {
+ if (!ReadSymbolicLink(stat_file, &exe_name)) {
// No such process. Happens frequently in e.g. TerminateAllChromeProcesses
return FilePath();
}
diff --git a/chromium/base/process/process_metrics.cc b/chromium/base/process/process_metrics.cc
index 127fb4649f7..83289b8c78b 100644
--- a/chromium/base/process/process_metrics.cc
+++ b/chromium/base/process/process_metrics.cc
@@ -42,4 +42,12 @@ scoped_ptr<Value> SystemMetrics::ToValue() const {
return res.PassAs<Value>();
}
+double ProcessMetrics::GetPlatformIndependentCPUUsage() {
+#if defined(OS_WIN)
+ return GetCPUUsage() * processor_count_;
+#else
+ return GetCPUUsage();
+#endif
+}
+
} // namespace base
diff --git a/chromium/base/process/process_metrics.h b/chromium/base/process/process_metrics.h
index f6b225fa94f..560490a9b8c 100644
--- a/chromium/base/process/process_metrics.h
+++ b/chromium/base/process/process_metrics.h
@@ -160,14 +160,19 @@ class BASE_EXPORT ProcessMetrics {
// load and fragmentation.
bool CalculateFreeMemory(FreeMBytes* free) const;
- // Returns the CPU usage in percent since the last time this method was
- // called. The first time this method is called it returns 0 and will return
- // the actual CPU info on subsequent calls.
- // On Windows, the CPU usage value is for all CPUs. So if you have 2 CPUs and
- // your process is using all the cycles of 1 CPU and not the other CPU, this
- // method returns 50.
+ // Returns the CPU usage in percent since the last time this method or
+ // GetPlatformIndependentCPUUsage() was called. The first time this method
+ // is called it returns 0 and will return the actual CPU info on subsequent
+ // calls. On Windows, the CPU usage value is for all CPUs. So if you have
+ // 2 CPUs and your process is using all the cycles of 1 CPU and not the other
+ // CPU, this method returns 50.
double GetCPUUsage();
+ // 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.
+ double GetPlatformIndependentCPUUsage();
+
// Retrieves accounting information for all I/O operations performed by the
// process.
// If IO information is retrieved successfully, the function returns true
@@ -273,6 +278,16 @@ struct BASE_EXPORT SystemMemoryInfoKB {
#endif
};
+// Parses a string containing the contents of /proc/meminfo
+// returns true on success or false for a parsing error
+BASE_EXPORT bool ParseProcMeminfo(const std::string& input,
+ SystemMemoryInfoKB* meminfo);
+
+// Parses a string containing the contents of /proc/vmstat
+// returns true on success or false for a parsing error
+BASE_EXPORT bool ParseProcVmstat(const std::string& input,
+ SystemMemoryInfoKB* meminfo);
+
// Retrieves data from /proc/meminfo and /proc/vmstat
// about system-wide memory consumption.
// Fills in the provided |meminfo| structure. Returns true on success.
diff --git a/chromium/base/process/process_metrics_linux.cc b/chromium/base/process/process_metrics_linux.cc
index 7f46639e4a4..afa88486a0b 100644
--- a/chromium/base/process/process_metrics_linux.cc
+++ b/chromium/base/process/process_metrics_linux.cc
@@ -405,33 +405,6 @@ int GetNumberOfThreads(ProcessHandle process) {
namespace {
-// The format of /proc/meminfo is:
-//
-// MemTotal: 8235324 kB
-// MemFree: 1628304 kB
-// Buffers: 429596 kB
-// Cached: 4728232 kB
-// ...
-const size_t kMemTotalIndex = 1;
-const size_t kMemFreeIndex = 4;
-const size_t kMemBuffersIndex = 7;
-const size_t kMemCachedIndex = 10;
-const size_t kMemActiveAnonIndex = 22;
-const size_t kMemInactiveAnonIndex = 25;
-const size_t kMemActiveFileIndex = 28;
-const size_t kMemInactiveFileIndex = 31;
-
-// The format of /proc/vmstat is:
-//
-// nr_free_pages 299878
-// nr_inactive_anon 239863
-// nr_active_anon 1318966
-// nr_inactive_file 2015629
-// ...
-const size_t kVMPagesSwappedIn = 75;
-const size_t kVMPagesSwappedOut = 77;
-const size_t kVMPageMajorFaults = 95;
-
// The format of /proc/diskstats is:
// Device major number
// Device minor number
@@ -538,6 +511,123 @@ scoped_ptr<Value> SystemMemoryInfoKB::ToValue() const {
return res.PassAs<Value>();
}
+// exposed for testing
+bool ParseProcMeminfo(const std::string& meminfo_data,
+ SystemMemoryInfoKB* meminfo) {
+ // The format of /proc/meminfo is:
+ //
+ // MemTotal: 8235324 kB
+ // MemFree: 1628304 kB
+ // Buffers: 429596 kB
+ // Cached: 4728232 kB
+ // ...
+ // There is no guarantee on the ordering or position
+ // though it doesn't appear to change very often
+
+ // As a basic sanity check, let's make sure we at least get non-zero
+ // MemTotal value
+ meminfo->total = 0;
+
+ std::vector<std::string> meminfo_lines;
+ Tokenize(meminfo_data, "\n", &meminfo_lines);
+ for (std::vector<std::string>::iterator it = meminfo_lines.begin();
+ it != meminfo_lines.end(); ++it) {
+ std::vector<std::string> tokens;
+ SplitStringAlongWhitespace(*it, &tokens);
+ // HugePages_* only has a number and no suffix so we can't rely on
+ // there being exactly 3 tokens.
+ if (tokens.size() > 1) {
+ if (tokens[0] == "MemTotal:") {
+ StringToInt(tokens[1], &meminfo->total);
+ continue;
+ } if (tokens[0] == "MemFree:") {
+ StringToInt(tokens[1], &meminfo->free);
+ continue;
+ } if (tokens[0] == "Buffers:") {
+ StringToInt(tokens[1], &meminfo->buffers);
+ continue;
+ } if (tokens[0] == "Cached:") {
+ StringToInt(tokens[1], &meminfo->cached);
+ continue;
+ } if (tokens[0] == "Active(anon):") {
+ StringToInt(tokens[1], &meminfo->active_anon);
+ continue;
+ } if (tokens[0] == "Inactive(anon):") {
+ StringToInt(tokens[1], &meminfo->inactive_anon);
+ continue;
+ } if (tokens[0] == "Active(file):") {
+ StringToInt(tokens[1], &meminfo->active_file);
+ continue;
+ } if (tokens[0] == "Inactive(file):") {
+ StringToInt(tokens[1], &meminfo->inactive_file);
+ continue;
+ } if (tokens[0] == "SwapTotal:") {
+ StringToInt(tokens[1], &meminfo->swap_total);
+ continue;
+ } if (tokens[0] == "SwapFree:") {
+ StringToInt(tokens[1], &meminfo->swap_free);
+ continue;
+ } if (tokens[0] == "Dirty:") {
+ StringToInt(tokens[1], &meminfo->dirty);
+ continue;
+#if defined(OS_CHROMEOS)
+ // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
+ // usually video memory otherwise invisible to the OS.
+ } if (tokens[0] == "Shmem:") {
+ StringToInt(tokens[1], &meminfo->shmem);
+ continue;
+ } if (tokens[0] == "Slab:") {
+ StringToInt(tokens[1], &meminfo->slab);
+ continue;
+#endif
+ }
+ } else
+ DLOG(WARNING) << "meminfo: tokens: " << tokens.size()
+ << " malformed line: " << *it;
+ }
+
+ // Make sure we got a valid MemTotal.
+ if (!meminfo->total)
+ return false;
+
+ return true;
+}
+
+// exposed for testing
+bool ParseProcVmstat(const std::string& vmstat_data,
+ SystemMemoryInfoKB* meminfo) {
+ // The format of /proc/vmstat is:
+ //
+ // nr_free_pages 299878
+ // nr_inactive_anon 239863
+ // nr_active_anon 1318966
+ // nr_inactive_file 2015629
+ // ...
+ //
+ // We iterate through the whole file because the position of the
+ // fields are dependent on the kernel version and configuration.
+
+ std::vector<std::string> vmstat_lines;
+ Tokenize(vmstat_data, "\n", &vmstat_lines);
+ for (std::vector<std::string>::iterator it = vmstat_lines.begin();
+ it != vmstat_lines.end(); ++it) {
+ std::vector<std::string> tokens;
+ SplitString(*it, ' ', &tokens);
+ if (tokens.size() == 2) {
+ if (tokens[0] == "pswpin") {
+ StringToInt(tokens[1], &meminfo->pswpin);
+ continue;
+ } if (tokens[0] == "pswpout") {
+ StringToInt(tokens[1], &meminfo->pswpout);
+ continue;
+ } if (tokens[0] == "pgmajfault")
+ StringToInt(tokens[1], &meminfo->pgmajfault);
+ }
+ }
+
+ return true;
+}
+
bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
// Synchronously reading files in /proc is safe.
ThreadRestrictions::ScopedAllowIO allow_io;
@@ -549,55 +639,13 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
DLOG(WARNING) << "Failed to open " << meminfo_file.value();
return false;
}
- std::vector<std::string> meminfo_fields;
- SplitStringAlongWhitespace(meminfo_data, &meminfo_fields);
- if (meminfo_fields.size() < kMemCachedIndex) {
- DLOG(WARNING) << "Failed to parse " << meminfo_file.value()
- << ". Only found " << meminfo_fields.size() << " fields.";
+ if (!ParseProcMeminfo(meminfo_data, meminfo)) {
+ DLOG(WARNING) << "Failed to parse " << meminfo_file.value();
return false;
}
- DCHECK_EQ(meminfo_fields[kMemTotalIndex-1], "MemTotal:");
- DCHECK_EQ(meminfo_fields[kMemFreeIndex-1], "MemFree:");
- DCHECK_EQ(meminfo_fields[kMemBuffersIndex-1], "Buffers:");
- DCHECK_EQ(meminfo_fields[kMemCachedIndex-1], "Cached:");
- DCHECK_EQ(meminfo_fields[kMemActiveAnonIndex-1], "Active(anon):");
- DCHECK_EQ(meminfo_fields[kMemInactiveAnonIndex-1], "Inactive(anon):");
- DCHECK_EQ(meminfo_fields[kMemActiveFileIndex-1], "Active(file):");
- DCHECK_EQ(meminfo_fields[kMemInactiveFileIndex-1], "Inactive(file):");
-
- StringToInt(meminfo_fields[kMemTotalIndex], &meminfo->total);
- StringToInt(meminfo_fields[kMemFreeIndex], &meminfo->free);
- StringToInt(meminfo_fields[kMemBuffersIndex], &meminfo->buffers);
- StringToInt(meminfo_fields[kMemCachedIndex], &meminfo->cached);
- StringToInt(meminfo_fields[kMemActiveAnonIndex], &meminfo->active_anon);
- StringToInt(meminfo_fields[kMemInactiveAnonIndex], &meminfo->inactive_anon);
- StringToInt(meminfo_fields[kMemActiveFileIndex], &meminfo->active_file);
- StringToInt(meminfo_fields[kMemInactiveFileIndex], &meminfo->inactive_file);
-
- // We don't know when these fields appear, so we must search for them.
- for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) {
- if (meminfo_fields[i] == "SwapTotal:")
- StringToInt(meminfo_fields[i+1], &meminfo->swap_total);
- if (meminfo_fields[i] == "SwapFree:")
- StringToInt(meminfo_fields[i+1], &meminfo->swap_free);
- if (meminfo_fields[i] == "Dirty:")
- StringToInt(meminfo_fields[i+1], &meminfo->dirty);
- }
-
#if defined(OS_CHROMEOS)
- // Chrome OS has a tweaked kernel that allows us to query Shmem, which is
- // usually video memory otherwise invisible to the OS. Unfortunately, the
- // meminfo format varies on different hardware so we have to search for the
- // string. It always appears after "Cached:".
- for (size_t i = kMemCachedIndex+2; i < meminfo_fields.size(); i += 3) {
- if (meminfo_fields[i] == "Shmem:")
- StringToInt(meminfo_fields[i+1], &meminfo->shmem);
- if (meminfo_fields[i] == "Slab:")
- StringToInt(meminfo_fields[i+1], &meminfo->slab);
- }
-
// Report on Chrome OS GEM object graphics memory. /var/run/debugfs_gpu is a
// bind mount into /sys/kernel/debug and synchronously reading the in-memory
// files in /sys is fast.
@@ -640,15 +688,10 @@ bool GetSystemMemoryInfo(SystemMemoryInfoKB* meminfo) {
DLOG(WARNING) << "Failed to open " << vmstat_file.value();
return false;
}
-
- std::vector<std::string> vmstat_fields;
- SplitStringAlongWhitespace(vmstat_data, &vmstat_fields);
- if (vmstat_fields[kVMPagesSwappedIn-1] == "pswpin")
- StringToInt(vmstat_fields[kVMPagesSwappedIn], &meminfo->pswpin);
- if (vmstat_fields[kVMPagesSwappedOut-1] == "pswpout")
- StringToInt(vmstat_fields[kVMPagesSwappedOut], &meminfo->pswpout);
- if (vmstat_fields[kVMPageMajorFaults-1] == "pgmajfault")
- StringToInt(vmstat_fields[kVMPageMajorFaults], &meminfo->pgmajfault);
+ if (!ParseProcVmstat(vmstat_data, meminfo)) {
+ DLOG(WARNING) << "Failed to parse " << vmstat_file.value();
+ return false;
+ }
return true;
}
diff --git a/chromium/base/process/process_metrics_unittest.cc b/chromium/base/process/process_metrics_unittest.cc
new file mode 100644
index 00000000000..5d365d9a203
--- /dev/null
+++ b/chromium/base/process/process_metrics_unittest.cc
@@ -0,0 +1,397 @@
+// 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/process/process_metrics.h"
+
+#include <sstream>
+#include <string>
+
+#include "base/threading/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace base {
+namespace debug {
+
+// Tests for SystemMetrics.
+// Exists as a class so it can be a friend of SystemMetrics.
+class SystemMetricsTest : public testing::Test {
+ public:
+ SystemMetricsTest() {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST_F(SystemMetricsTest, IsValidDiskName) {
+ std::string invalid_input1 = "";
+ std::string invalid_input2 = "s";
+ std::string invalid_input3 = "sdz+";
+ std::string invalid_input4 = "hda0";
+ std::string invalid_input5 = "mmcbl";
+ std::string invalid_input6 = "mmcblka";
+ std::string invalid_input7 = "mmcblkb";
+ std::string invalid_input8 = "mmmblk0";
+
+ EXPECT_FALSE(IsValidDiskName(invalid_input1));
+ EXPECT_FALSE(IsValidDiskName(invalid_input2));
+ EXPECT_FALSE(IsValidDiskName(invalid_input3));
+ EXPECT_FALSE(IsValidDiskName(invalid_input4));
+ EXPECT_FALSE(IsValidDiskName(invalid_input5));
+ EXPECT_FALSE(IsValidDiskName(invalid_input6));
+ EXPECT_FALSE(IsValidDiskName(invalid_input7));
+ EXPECT_FALSE(IsValidDiskName(invalid_input8));
+
+ std::string valid_input1 = "sda";
+ std::string valid_input2 = "sdaaaa";
+ std::string valid_input3 = "hdz";
+ std::string valid_input4 = "mmcblk0";
+ std::string valid_input5 = "mmcblk999";
+
+ EXPECT_TRUE(IsValidDiskName(valid_input1));
+ EXPECT_TRUE(IsValidDiskName(valid_input2));
+ EXPECT_TRUE(IsValidDiskName(valid_input3));
+ EXPECT_TRUE(IsValidDiskName(valid_input4));
+ EXPECT_TRUE(IsValidDiskName(valid_input5));
+}
+
+TEST_F(SystemMetricsTest, ParseMeminfo) {
+ struct SystemMemoryInfoKB meminfo;
+ std::string invalid_input1 = "abc";
+ std::string invalid_input2 = "MemTotal:";
+ // Partial file with no MemTotal
+ std::string invalid_input3 =
+ "MemFree: 3913968 kB\n"
+ "Buffers: 2348340 kB\n"
+ "Cached: 49071596 kB\n"
+ "SwapCached: 12 kB\n"
+ "Active: 36393900 kB\n"
+ "Inactive: 21221496 kB\n"
+ "Active(anon): 5674352 kB\n"
+ "Inactive(anon): 633992 kB\n";
+ EXPECT_FALSE(ParseProcMeminfo(invalid_input1, &meminfo));
+ EXPECT_FALSE(ParseProcMeminfo(invalid_input2, &meminfo));
+ EXPECT_FALSE(ParseProcMeminfo(invalid_input3, &meminfo));
+
+ std::string valid_input1 =
+ "MemTotal: 3981504 kB\n"
+ "MemFree: 140764 kB\n"
+ "Buffers: 116480 kB\n"
+ "Cached: 406160 kB\n"
+ "SwapCached: 21304 kB\n"
+ "Active: 3152040 kB\n"
+ "Inactive: 472856 kB\n"
+ "Active(anon): 2972352 kB\n"
+ "Inactive(anon): 270108 kB\n"
+ "Active(file): 179688 kB\n"
+ "Inactive(file): 202748 kB\n"
+ "Unevictable: 0 kB\n"
+ "Mlocked: 0 kB\n"
+ "SwapTotal: 5832280 kB\n"
+ "SwapFree: 3672368 kB\n"
+ "Dirty: 184 kB\n"
+ "Writeback: 0 kB\n"
+ "AnonPages: 3101224 kB\n"
+ "Mapped: 142296 kB\n"
+ "Shmem: 140204 kB\n"
+ "Slab: 54212 kB\n"
+ "SReclaimable: 30936 kB\n"
+ "SUnreclaim: 23276 kB\n"
+ "KernelStack: 2464 kB\n"
+ "PageTables: 24812 kB\n"
+ "NFS_Unstable: 0 kB\n"
+ "Bounce: 0 kB\n"
+ "WritebackTmp: 0 kB\n"
+ "CommitLimit: 7823032 kB\n"
+ "Committed_AS: 7973536 kB\n"
+ "VmallocTotal: 34359738367 kB\n"
+ "VmallocUsed: 375940 kB\n"
+ "VmallocChunk: 34359361127 kB\n"
+ "DirectMap4k: 72448 kB\n"
+ "DirectMap2M: 4061184 kB\n";
+ // output from a much older kernel where the Active and Inactive aren't
+ // broken down into anon and file and Huge Pages are enabled
+ std::string valid_input2 =
+ "MemTotal: 255908 kB\n"
+ "MemFree: 69936 kB\n"
+ "Buffers: 15812 kB\n"
+ "Cached: 115124 kB\n"
+ "SwapCached: 0 kB\n"
+ "Active: 92700 kB\n"
+ "Inactive: 63792 kB\n"
+ "HighTotal: 0 kB\n"
+ "HighFree: 0 kB\n"
+ "LowTotal: 255908 kB\n"
+ "LowFree: 69936 kB\n"
+ "SwapTotal: 524280 kB\n"
+ "SwapFree: 524200 kB\n"
+ "Dirty: 4 kB\n"
+ "Writeback: 0 kB\n"
+ "Mapped: 42236 kB\n"
+ "Slab: 25912 kB\n"
+ "Committed_AS: 118680 kB\n"
+ "PageTables: 1236 kB\n"
+ "VmallocTotal: 3874808 kB\n"
+ "VmallocUsed: 1416 kB\n"
+ "VmallocChunk: 3872908 kB\n"
+ "HugePages_Total: 0\n"
+ "HugePages_Free: 0\n"
+ "Hugepagesize: 4096 kB\n";
+
+ EXPECT_TRUE(ParseProcMeminfo(valid_input1, &meminfo));
+ EXPECT_TRUE(meminfo.total == 3981504);
+ EXPECT_TRUE(meminfo.free == 140764);
+ EXPECT_TRUE(meminfo.buffers == 116480);
+ EXPECT_TRUE(meminfo.cached == 406160);
+ EXPECT_TRUE(meminfo.active_anon == 2972352);
+ EXPECT_TRUE(meminfo.active_file == 179688);
+ EXPECT_TRUE(meminfo.inactive_anon == 270108);
+ EXPECT_TRUE(meminfo.inactive_file == 202748);
+ EXPECT_TRUE(meminfo.swap_total == 5832280);
+ EXPECT_TRUE(meminfo.swap_free == 3672368);
+ EXPECT_TRUE(meminfo.dirty == 184);
+#if defined(OS_CHROMEOS)
+ EXPECT_TRUE(meminfo.shmem == 140204);
+ EXPECT_TRUE(meminfo.slab == 54212);
+#endif
+ EXPECT_TRUE(ParseProcMeminfo(valid_input2, &meminfo));
+ EXPECT_TRUE(meminfo.total == 255908);
+ EXPECT_TRUE(meminfo.free == 69936);
+ EXPECT_TRUE(meminfo.buffers == 15812);
+ EXPECT_TRUE(meminfo.cached == 115124);
+ EXPECT_TRUE(meminfo.swap_total == 524280);
+ EXPECT_TRUE(meminfo.swap_free == 524200);
+ EXPECT_TRUE(meminfo.dirty == 4);
+}
+
+TEST_F(SystemMetricsTest, ParseVmstat) {
+ struct SystemMemoryInfoKB meminfo;
+ // part of vmstat from a 3.2 kernel with numa enabled
+ std::string valid_input1 =
+ "nr_free_pages 905104\n"
+ "nr_inactive_anon 142478"
+ "nr_active_anon 1520046\n"
+ "nr_inactive_file 4481001\n"
+ "nr_active_file 8313439\n"
+ "nr_unevictable 5044\n"
+ "nr_mlock 5044\n"
+ "nr_anon_pages 1633780\n"
+ "nr_mapped 104742\n"
+ "nr_file_pages 12828218\n"
+ "nr_dirty 245\n"
+ "nr_writeback 0\n"
+ "nr_slab_reclaimable 831609\n"
+ "nr_slab_unreclaimable 41164\n"
+ "nr_page_table_pages 31470\n"
+ "nr_kernel_stack 1735\n"
+ "nr_unstable 0\n"
+ "nr_bounce 0\n"
+ "nr_vmscan_write 406\n"
+ "nr_vmscan_immediate_reclaim 281\n"
+ "nr_writeback_temp 0\n"
+ "nr_isolated_anon 0\n"
+ "nr_isolated_file 0\n"
+ "nr_shmem 28820\n"
+ "nr_dirtied 84674644\n"
+ "nr_written 75307109\n"
+ "nr_anon_transparent_hugepages 0\n"
+ "nr_dirty_threshold 1536206\n"
+ "nr_dirty_background_threshold 768103\n"
+ "pgpgin 30777108\n"
+ "pgpgout 319023278\n"
+ "pswpin 179\n"
+ "pswpout 406\n"
+ "pgalloc_dma 0\n"
+ "pgalloc_dma32 20833399\n"
+ "pgalloc_normal 1622609290\n"
+ "pgalloc_movable 0\n"
+ "pgfree 1644355583\n"
+ "pgactivate 75391882\n"
+ "pgdeactivate 4121019\n"
+ "pgfault 2542879679\n"
+ "pgmajfault 487192\n";
+ std::string valid_input2 =
+ "nr_free_pages 180125\n"
+ "nr_inactive_anon 51\n"
+ "nr_active_anon 38832\n"
+ "nr_inactive_file 50171\n"
+ "nr_active_file 47510\n"
+ "nr_unevictable 0\n"
+ "nr_mlock 0\n"
+ "nr_anon_pages 38825\n"
+ "nr_mapped 24043\n"
+ "nr_file_pages 97733\n"
+ "nr_dirty 0\n"
+ "nr_writeback 0\n"
+ "nr_slab_reclaimable 4032\n"
+ "nr_slab_unreclaimable 2848\n"
+ "nr_page_table_pages 1505\n"
+ "nr_kernel_stack 626\n"
+ "nr_unstable 0\n"
+ "nr_bounce 0\n"
+ "nr_vmscan_write 0\n"
+ "nr_vmscan_immediate_reclaim 0\n"
+ "nr_writeback_temp 0\n"
+ "nr_isolated_anon 0\n"
+ "nr_isolated_file 0\n"
+ "nr_shmem 58\n"
+ "nr_dirtied 435358\n"
+ "nr_written 401258\n"
+ "nr_anon_transparent_hugepages 0\n"
+ "nr_dirty_threshold 18566\n"
+ "nr_dirty_background_threshold 4641\n"
+ "pgpgin 299464\n"
+ "pgpgout 2437788\n"
+ "pswpin 12\n"
+ "pswpout 901\n"
+ "pgalloc_normal 144213030\n"
+ "pgalloc_high 164501274\n"
+ "pgalloc_movable 0\n"
+ "pgfree 308894908\n"
+ "pgactivate 239320\n"
+ "pgdeactivate 1\n"
+ "pgfault 716044601\n"
+ "pgmajfault 2023\n"
+ "pgrefill_normal 0\n"
+ "pgrefill_high 0\n"
+ "pgrefill_movable 0\n";
+ EXPECT_TRUE(ParseProcVmstat(valid_input1, &meminfo));
+ EXPECT_TRUE(meminfo.pswpin == 179);
+ EXPECT_TRUE(meminfo.pswpout == 406);
+ EXPECT_TRUE(meminfo.pgmajfault == 487192);
+ EXPECT_TRUE(ParseProcVmstat(valid_input2, &meminfo));
+ EXPECT_TRUE(meminfo.pswpin == 12);
+ EXPECT_TRUE(meminfo.pswpout == 901);
+ EXPECT_TRUE(meminfo.pgmajfault == 2023);
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST(SystemMetrics2Test, GetSystemMemoryInfo) {
+ base::SystemMemoryInfoKB info;
+ EXPECT_TRUE(base::GetSystemMemoryInfo(&info));
+
+ // Ensure each field received a value.
+ EXPECT_GT(info.total, 0);
+ EXPECT_GT(info.free, 0);
+ EXPECT_GT(info.buffers, 0);
+ EXPECT_GT(info.cached, 0);
+ EXPECT_GT(info.active_anon, 0);
+ EXPECT_GT(info.inactive_anon, 0);
+ EXPECT_GT(info.active_file, 0);
+ EXPECT_GT(info.inactive_file, 0);
+
+ // All the values should be less than the total amount of memory.
+ EXPECT_LT(info.free, info.total);
+ EXPECT_LT(info.buffers, info.total);
+ EXPECT_LT(info.cached, info.total);
+ EXPECT_LT(info.active_anon, info.total);
+ EXPECT_LT(info.inactive_anon, info.total);
+ EXPECT_LT(info.active_file, info.total);
+ EXPECT_LT(info.inactive_file, info.total);
+
+#if defined(OS_CHROMEOS)
+ // Chrome OS exposes shmem.
+ EXPECT_GT(info.shmem, 0);
+ EXPECT_LT(info.shmem, info.total);
+ // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects
+ // and gem_size cannot be tested here.
+#endif
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+#if defined(OS_WIN)
+// TODO(estade): if possible, port this test.
+TEST(ProcessMetricsTest, CalcFreeMemory) {
+ scoped_ptr<base::ProcessMetrics> metrics(
+ base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess()));
+ ASSERT_TRUE(NULL != metrics.get());
+
+ bool using_tcmalloc = false;
+
+ // Detect if we are using tcmalloc
+#if !defined(NO_TCMALLOC)
+ const char* chrome_allocator = getenv("CHROME_ALLOCATOR");
+ if (!chrome_allocator || _stricmp(chrome_allocator, "tcmalloc") == 0)
+ using_tcmalloc = true;
+#endif
+
+ // Typical values here is ~1900 for total and ~1000 for largest. Obviously
+ // it depends in what other tests have done to this process.
+ base::FreeMBytes free_mem1 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1));
+ EXPECT_LT(10u, free_mem1.total);
+ EXPECT_LT(10u, free_mem1.largest);
+ EXPECT_GT(2048u, free_mem1.total);
+ EXPECT_GT(2048u, free_mem1.largest);
+ EXPECT_GE(free_mem1.total, free_mem1.largest);
+ EXPECT_TRUE(NULL != free_mem1.largest_ptr);
+
+ // Allocate 20M and check again. It should have gone down.
+ const int kAllocMB = 20;
+ scoped_ptr<char[]> alloc(new char[kAllocMB * 1024 * 1024]);
+ size_t expected_total = free_mem1.total - kAllocMB;
+ size_t expected_largest = free_mem1.largest;
+
+ base::FreeMBytes free_mem2 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
+ EXPECT_GE(free_mem2.total, free_mem2.largest);
+ // This test is flaky when using tcmalloc, because tcmalloc
+ // allocation strategy sometimes results in less than the
+ // full drop of 20Mb of free memory.
+ if (!using_tcmalloc)
+ EXPECT_GE(expected_total, free_mem2.total);
+ EXPECT_GE(expected_largest, free_mem2.largest);
+ EXPECT_TRUE(NULL != free_mem2.largest_ptr);
+}
+#endif // defined(OS_WIN)
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+TEST(ProcessMetricsTest, ParseProcStatCPU) {
+ // /proc/self/stat for a process running "top".
+ const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 "
+ "4202496 471 0 0 0 "
+ "12 16 0 0 " // <- These are the goods.
+ "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 "
+ "4246868 140733983044336 18446744073709551615 140244213071219 "
+ "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0";
+ EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat));
+
+ // cat /proc/self/stat on a random other machine I have.
+ const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 "
+ "0 142 0 0 0 "
+ "0 0 0 0 " // <- No CPU, apparently.
+ "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 "
+ "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
+
+ EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat));
+}
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+
+// Disable on Android because base_unittests runs inside a Dalvik VM that
+// starts and stop threads (crbug.com/175563).
+#if defined(OS_LINUX)
+TEST(ProcessMetricsTest, GetNumberOfThreads) {
+ const base::ProcessHandle current = base::GetCurrentProcessHandle();
+ const int initial_threads = base::GetNumberOfThreads(current);
+ ASSERT_GT(initial_threads, 0);
+ const int kNumAdditionalThreads = 10;
+ {
+ scoped_ptr<base::Thread> my_threads[kNumAdditionalThreads];
+ for (int i = 0; i < kNumAdditionalThreads; ++i) {
+ my_threads[i].reset(new base::Thread("GetNumberOfThreadsTest"));
+ my_threads[i]->Start();
+ ASSERT_EQ(base::GetNumberOfThreads(current), initial_threads + 1 + i);
+ }
+ }
+ // The Thread destructor will stop them.
+ ASSERT_EQ(initial_threads, base::GetNumberOfThreads(current));
+}
+#endif // defined(OS_LINUX)
+
+} // namespace debug
+} // namespace base
diff --git a/chromium/base/process/process_util_unittest_ios.cc b/chromium/base/process/process_metrics_unittest_ios.cc
index cad0f1b09f0..3e1ca35b18c 100644
--- a/chromium/base/process/process_util_unittest_ios.cc
+++ b/chromium/base/process/process_metrics_unittest_ios.cc
@@ -1,12 +1,13 @@
-// Copyright (c) 2012 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.
-#include "base/memory/scoped_ptr.h"
#include "base/process/process_metrics.h"
+
+#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
-TEST(ProcessUtilTestIos, Memory) {
+TEST(ProcessMetricsTestIos, Memory) {
scoped_ptr<base::ProcessMetrics> process_metrics(
base::ProcessMetrics::CreateProcessMetrics(
base::GetCurrentProcessHandle()));
diff --git a/chromium/base/process/process_metrics_unittests.cc b/chromium/base/process/process_metrics_unittests.cc
deleted file mode 100644
index 387d860eda5..00000000000
--- a/chromium/base/process/process_metrics_unittests.cc
+++ /dev/null
@@ -1,63 +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/process/process_metrics.h"
-
-#include <sstream>
-#include <string>
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-
-namespace base {
-namespace debug {
-
-// Tests for SystemMetrics.
-// Exists as a class so it can be a friend of SystemMetrics.
-class SystemMetricsTest : public testing::Test {
- public:
- SystemMetricsTest() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(SystemMetricsTest);
-};
-
-/////////////////////////////////////////////////////////////////////////////
-
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-TEST_F(SystemMetricsTest, IsValidDiskName) {
- std::string invalid_input1 = "";
- std::string invalid_input2 = "s";
- std::string invalid_input3 = "sdz+";
- std::string invalid_input4 = "hda0";
- std::string invalid_input5 = "mmcbl";
- std::string invalid_input6 = "mmcblka";
- std::string invalid_input7 = "mmcblkb";
- std::string invalid_input8 = "mmmblk0";
-
- EXPECT_FALSE(IsValidDiskName(invalid_input1));
- EXPECT_FALSE(IsValidDiskName(invalid_input2));
- EXPECT_FALSE(IsValidDiskName(invalid_input3));
- EXPECT_FALSE(IsValidDiskName(invalid_input4));
- EXPECT_FALSE(IsValidDiskName(invalid_input5));
- EXPECT_FALSE(IsValidDiskName(invalid_input6));
- EXPECT_FALSE(IsValidDiskName(invalid_input7));
- EXPECT_FALSE(IsValidDiskName(invalid_input8));
-
- std::string valid_input1 = "sda";
- std::string valid_input2 = "sdaaaa";
- std::string valid_input3 = "hdz";
- std::string valid_input4 = "mmcblk0";
- std::string valid_input5 = "mmcblk999";
-
- EXPECT_TRUE(IsValidDiskName(valid_input1));
- EXPECT_TRUE(IsValidDiskName(valid_input2));
- EXPECT_TRUE(IsValidDiskName(valid_input3));
- EXPECT_TRUE(IsValidDiskName(valid_input4));
- EXPECT_TRUE(IsValidDiskName(valid_input5));
-}
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-} // namespace debug
-} // namespace base
diff --git a/chromium/base/process/process_util_unittest.cc b/chromium/base/process/process_util_unittest.cc
index 44be9f4a273..6bfc1d07388 100644
--- a/chromium/base/process/process_util_unittest.cc
+++ b/chromium/base/process/process_util_unittest.cc
@@ -19,7 +19,9 @@
#include "base/process/memory.h"
#include "base/process/process.h"
#include "base/process/process_metrics.h"
+#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/synchronization/waitable_event.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
@@ -29,7 +31,6 @@
#include "testing/multiprocess_func_list.h"
#if defined(OS_LINUX)
-#include <glib.h>
#include <malloc.h>
#include <sched.h>
#endif
@@ -44,6 +45,7 @@
#endif
#if defined(OS_WIN)
#include <windows.h>
+#include "base/win/windows_version.h"
#endif
#if defined(OS_MACOSX)
#include <mach/vm_param.h>
@@ -54,12 +56,6 @@ using base::FilePath;
namespace {
-#if defined(OS_WIN)
-const wchar_t kProcessName[] = L"base_unittests.exe";
-#else
-const wchar_t kProcessName[] = L"base_unittests";
-#endif // defined(OS_WIN)
-
#if defined(OS_ANDROID)
const char kShellPath[] = "/system/bin/sh";
const char kPosixShell[] = "sh";
@@ -69,7 +65,6 @@ const char kPosixShell[] = "bash";
#endif
const char kSignalFileSlow[] = "SlowChildProcess.die";
-const char kSignalFileCrash[] = "CrashingChildProcess.die";
const char kSignalFileKill[] = "KilledChildProcess.die";
#if defined(OS_WIN)
@@ -221,6 +216,7 @@ TEST_F(ProcessUtilTest, GetProcId) {
// TODO(gspencer): turn this test process into a very small program
// with no symbols (instead of using the multiprocess testing
// framework) to reduce the ReportCrash overhead.
+const char kSignalFileCrash[] = "CrashingChildProcess.die";
MULTIPROCESS_TEST_MAIN(CrashingChildProcess) {
WaitToDie(ProcessUtilTest::GetSignalFilePath(kSignalFileCrash).c_str());
@@ -360,85 +356,8 @@ TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) {
EXPECT_EQ(old_priority, new_priority);
}
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-TEST_F(ProcessUtilTest, GetSystemMemoryInfo) {
- base::SystemMemoryInfoKB info;
- EXPECT_TRUE(base::GetSystemMemoryInfo(&info));
-
- // Ensure each field received a value.
- EXPECT_GT(info.total, 0);
- EXPECT_GT(info.free, 0);
- EXPECT_GT(info.buffers, 0);
- EXPECT_GT(info.cached, 0);
- EXPECT_GT(info.active_anon, 0);
- EXPECT_GT(info.inactive_anon, 0);
- EXPECT_GT(info.active_file, 0);
- EXPECT_GT(info.inactive_file, 0);
-
- // All the values should be less than the total amount of memory.
- EXPECT_LT(info.free, info.total);
- EXPECT_LT(info.buffers, info.total);
- EXPECT_LT(info.cached, info.total);
- EXPECT_LT(info.active_anon, info.total);
- EXPECT_LT(info.inactive_anon, info.total);
- EXPECT_LT(info.active_file, info.total);
- EXPECT_LT(info.inactive_file, info.total);
-
-#if defined(OS_CHROMEOS)
- // Chrome OS exposes shmem.
- EXPECT_GT(info.shmem, 0);
- EXPECT_LT(info.shmem, info.total);
- // Chrome unit tests are not run on actual Chrome OS hardware, so gem_objects
- // and gem_size cannot be tested here.
-#endif
-}
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
-// TODO(estade): if possible, port these 2 tests.
#if defined(OS_WIN)
-TEST_F(ProcessUtilTest, CalcFreeMemory) {
- scoped_ptr<base::ProcessMetrics> metrics(
- base::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess()));
- ASSERT_TRUE(NULL != metrics.get());
-
- bool using_tcmalloc = false;
-
- // Detect if we are using tcmalloc
-#if !defined(NO_TCMALLOC)
- const char* chrome_allocator = getenv("CHROME_ALLOCATOR");
- if (!chrome_allocator || _stricmp(chrome_allocator, "tcmalloc") == 0)
- using_tcmalloc = true;
-#endif
-
- // Typical values here is ~1900 for total and ~1000 for largest. Obviously
- // it depends in what other tests have done to this process.
- base::FreeMBytes free_mem1 = {0};
- EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1));
- EXPECT_LT(10u, free_mem1.total);
- EXPECT_LT(10u, free_mem1.largest);
- EXPECT_GT(2048u, free_mem1.total);
- EXPECT_GT(2048u, free_mem1.largest);
- EXPECT_GE(free_mem1.total, free_mem1.largest);
- EXPECT_TRUE(NULL != free_mem1.largest_ptr);
-
- // Allocate 20M and check again. It should have gone down.
- const int kAllocMB = 20;
- scoped_ptr<char[]> alloc(new char[kAllocMB * 1024 * 1024]);
- size_t expected_total = free_mem1.total - kAllocMB;
- size_t expected_largest = free_mem1.largest;
-
- base::FreeMBytes free_mem2 = {0};
- EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
- EXPECT_GE(free_mem2.total, free_mem2.largest);
- // This test is flaky when using tcmalloc, because tcmalloc
- // allocation strategy sometimes results in less than the
- // full drop of 20Mb of free memory.
- if (!using_tcmalloc)
- EXPECT_GE(expected_total, free_mem2.total);
- EXPECT_GE(expected_largest, free_mem2.largest);
- EXPECT_TRUE(NULL != free_mem2.largest_ptr);
-}
-
+// TODO(estade): if possible, port this test.
TEST_F(ProcessUtilTest, GetAppOutput) {
// Let's create a decently long message.
std::string message;
@@ -468,16 +387,64 @@ TEST_F(ProcessUtilTest, GetAppOutput) {
EXPECT_EQ("", output);
}
+// TODO(estade): if possible, port this test.
TEST_F(ProcessUtilTest, LaunchAsUser) {
base::UserTokenHandle token;
ASSERT_TRUE(OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token));
- std::wstring cmdline =
- this->MakeCmdLine("SimpleChildProcess", false).GetCommandLineString();
base::LaunchOptions options;
options.as_user = token;
- EXPECT_TRUE(base::LaunchProcess(cmdline, options, NULL));
+ EXPECT_TRUE(base::LaunchProcess(
+ this->MakeCmdLine("SimpleChildProcess", false), options, NULL));
+}
+
+static const char kEventToTriggerHandleSwitch[] = "event-to-trigger-handle";
+
+MULTIPROCESS_TEST_MAIN(TriggerEventChildProcess) {
+ std::string handle_value_string =
+ CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ kEventToTriggerHandleSwitch);
+ CHECK(!handle_value_string.empty());
+
+ uint64 handle_value_uint64;
+ CHECK(base::StringToUint64(handle_value_string, &handle_value_uint64));
+ // Give ownership of the handle to |event|.
+ base::WaitableEvent event(reinterpret_cast<HANDLE>(handle_value_uint64));
+
+ event.Signal();
+
+ return 0;
}
+TEST_F(ProcessUtilTest, InheritSpecifiedHandles) {
+ // Manually create the event, so that it can be inheritable.
+ SECURITY_ATTRIBUTES security_attributes = {};
+ security_attributes.nLength = static_cast<DWORD>(sizeof(security_attributes));
+ security_attributes.lpSecurityDescriptor = NULL;
+ security_attributes.bInheritHandle = true;
+
+ // Takes ownership of the event handle.
+ base::WaitableEvent event(
+ CreateEvent(&security_attributes, true, false, NULL));
+ base::HandlesToInheritVector handles_to_inherit;
+ handles_to_inherit.push_back(event.handle());
+ base::LaunchOptions options;
+ options.handles_to_inherit = &handles_to_inherit;
+
+ CommandLine cmd_line = MakeCmdLine("TriggerEventChildProcess", false);
+ cmd_line.AppendSwitchASCII(kEventToTriggerHandleSwitch,
+ base::Uint64ToString(reinterpret_cast<uint64>(event.handle())));
+
+ // This functionality actually requires Vista or later. Make sure that it
+ // fails properly on XP.
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ EXPECT_FALSE(base::LaunchProcess(cmd_line, options, NULL));
+ return;
+ }
+
+ // Launch the process and wait for it to trigger the event.
+ ASSERT_TRUE(base::LaunchProcess(cmd_line, options, NULL));
+ EXPECT_TRUE(event.TimedWait(TestTimeouts::action_max_timeout()));
+}
#endif // defined(OS_WIN)
#if defined(OS_POSIX)
@@ -525,7 +492,7 @@ MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) {
int written = HANDLE_EINTR(write(write_pipe, &num_open_files,
sizeof(num_open_files)));
DCHECK_EQ(static_cast<size_t>(written), sizeof(num_open_files));
- int ret = HANDLE_EINTR(close(write_pipe));
+ int ret = IGNORE_EINTR(close(write_pipe));
DPCHECK(ret == 0);
return 0;
@@ -541,7 +508,7 @@ int ProcessUtilTest::CountOpenFDsInChild() {
base::ProcessHandle handle = this->SpawnChild(
"ProcessUtilsLeakFDChildProcess", fd_mapping_vec, false);
CHECK(handle);
- int ret = HANDLE_EINTR(close(fds[1]));
+ int ret = IGNORE_EINTR(close(fds[1]));
DPCHECK(ret == 0);
// Read number of open files in client process from pipe;
@@ -557,7 +524,7 @@ int ProcessUtilTest::CountOpenFDsInChild() {
CHECK(base::WaitForSingleProcess(handle, base::TimeDelta::FromSeconds(1)));
#endif
base::CloseProcessHandle(handle);
- ret = HANDLE_EINTR(close(fds[0]));
+ ret = IGNORE_EINTR(close(fds[0]));
DPCHECK(ret == 0);
return num_open_files;
@@ -585,11 +552,11 @@ TEST_F(ProcessUtilTest, MAYBE_FDRemapping) {
ASSERT_EQ(fds_after, fds_before);
int ret;
- ret = HANDLE_EINTR(close(sockets[0]));
+ ret = IGNORE_EINTR(close(sockets[0]));
DPCHECK(ret == 0);
- ret = HANDLE_EINTR(close(sockets[1]));
+ ret = IGNORE_EINTR(close(sockets[1]));
DPCHECK(ret == 0);
- ret = HANDLE_EINTR(close(dev_null));
+ ret = IGNORE_EINTR(close(dev_null));
DPCHECK(ret == 0);
}
@@ -618,13 +585,13 @@ std::string TestLaunchProcess(const base::EnvironmentMap& env_changes,
CHECK_EQ(0, clone_flags);
#endif // OS_LINUX
EXPECT_TRUE(base::LaunchProcess(args, options, NULL));
- PCHECK(HANDLE_EINTR(close(fds[1])) == 0);
+ PCHECK(IGNORE_EINTR(close(fds[1])) == 0);
char buf[512];
const ssize_t n = HANDLE_EINTR(read(fds[0], buf, sizeof(buf)));
PCHECK(n > 0);
- PCHECK(HANDLE_EINTR(close(fds[0])) == 0);
+ PCHECK(IGNORE_EINTR(close(fds[0])) == 0);
return std::string(buf, n);
}
@@ -775,7 +742,16 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedSIGPIPE) {
}
#endif
-TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) {
+#if defined(ADDRESS_SANITIZER) && defined(OS_MACOSX) && \
+ defined(ARCH_CPU_64_BITS)
+// Times out under AddressSanitizer on 64-bit OS X, see
+// http://crbug.com/298197.
+#define MAYBE_GetAppOutputRestrictedNoZombies \
+ DISABLED_GetAppOutputRestrictedNoZombies
+#else
+#define MAYBE_GetAppOutputRestrictedNoZombies GetAppOutputRestrictedNoZombies
+#endif
+TEST_F(ProcessUtilTest, MAYBE_GetAppOutputRestrictedNoZombies) {
std::vector<std::string> argv;
argv.push_back(std::string(kShellPath)); // argv[0]
@@ -826,50 +802,6 @@ TEST_F(ProcessUtilTest, GetParentProcessId) {
EXPECT_EQ(ppid, getppid());
}
-#if defined(OS_LINUX) || defined(OS_ANDROID)
-TEST_F(ProcessUtilTest, ParseProcStatCPU) {
- // /proc/self/stat for a process running "top".
- const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 "
- "4202496 471 0 0 0 "
- "12 16 0 0 " // <- These are the goods.
- "20 0 1 0 121946157 15077376 314 18446744073709551615 4194304 "
- "4246868 140733983044336 18446744073709551615 140244213071219 "
- "0 0 0 138047495 0 0 0 17 1 0 0 0 0 0";
- EXPECT_EQ(12 + 16, base::ParseProcStatCPU(kTopStat));
-
- // cat /proc/self/stat on a random other machine I have.
- const char kSelfStat[] = "5364 (cat) R 5354 5364 5354 34819 5364 "
- "0 142 0 0 0 "
- "0 0 0 0 " // <- No CPU, apparently.
- "16 0 1 0 1676099790 2957312 114 4294967295 134512640 134528148 "
- "3221224832 3221224344 3086339742 0 0 0 0 0 0 0 17 0 0 0";
-
- EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat));
-}
-
-// Disable on Android because base_unittests runs inside a Dalvik VM that
-// starts and stop threads (crbug.com/175563).
-#if !defined(OS_ANDROID)
-TEST_F(ProcessUtilTest, GetNumberOfThreads) {
- const base::ProcessHandle current = base::GetCurrentProcessHandle();
- const int initial_threads = base::GetNumberOfThreads(current);
- ASSERT_GT(initial_threads, 0);
- const int kNumAdditionalThreads = 10;
- {
- scoped_ptr<base::Thread> my_threads[kNumAdditionalThreads];
- for (int i = 0; i < kNumAdditionalThreads; ++i) {
- my_threads[i].reset(new base::Thread("GetNumberOfThreadsTest"));
- my_threads[i]->Start();
- ASSERT_EQ(base::GetNumberOfThreads(current), initial_threads + 1 + i);
- }
- }
- // The Thread destructor will stop them.
- ASSERT_EQ(initial_threads, base::GetNumberOfThreads(current));
-}
-#endif // !defined(OS_ANDROID)
-
-#endif // defined(OS_LINUX) || defined(OS_ANDROID)
-
// TODO(port): port those unit tests.
bool IsProcessDead(base::ProcessHandle child) {
// waitpid() will actually reap the process which is exactly NOT what we
diff --git a/chromium/base/rand_util.cc b/chromium/base/rand_util.cc
index da6de87dc34..9641ff4f9c7 100644
--- a/chromium/base/rand_util.cc
+++ b/chromium/base/rand_util.cc
@@ -6,6 +6,7 @@
#include <math.h>
+#include <algorithm>
#include <limits>
#include "base/basictypes.h"
diff --git a/chromium/base/rand_util_posix.cc b/chromium/base/rand_util_posix.cc
index 9b18777b990..082d64923d5 100644
--- a/chromium/base/rand_util_posix.cc
+++ b/chromium/base/rand_util_posix.cc
@@ -46,9 +46,8 @@ uint64 RandUint64() {
uint64 number;
int urandom_fd = g_urandom_fd.Pointer()->fd();
- bool success = file_util::ReadFromFD(urandom_fd,
- reinterpret_cast<char*>(&number),
- sizeof(number));
+ bool success = ReadFromFD(urandom_fd, reinterpret_cast<char*>(&number),
+ sizeof(number));
CHECK(success);
return number;
diff --git a/chromium/base/run_loop.cc b/chromium/base/run_loop.cc
index 2e80ff29e01..8666ee448fe 100644
--- a/chromium/base/run_loop.cc
+++ b/chromium/base/run_loop.cc
@@ -10,13 +10,13 @@ namespace base {
RunLoop::RunLoop()
: loop_(MessageLoop::current()),
- weak_factory_(this),
previous_run_loop_(NULL),
run_depth_(0),
run_called_(false),
quit_called_(false),
running_(false),
- quit_when_idle_received_(false) {
+ quit_when_idle_received_(false),
+ weak_factory_(this) {
#if !defined(OS_MACOSX) && !defined(OS_ANDROID) && \
!defined(USE_GTK_MESSAGE_PUMP)
dispatcher_ = NULL;
@@ -27,14 +27,14 @@ RunLoop::RunLoop()
!defined(USE_GTK_MESSAGE_PUMP)
RunLoop::RunLoop(MessageLoop::Dispatcher* dispatcher)
: loop_(MessageLoop::current()),
- weak_factory_(this),
previous_run_loop_(NULL),
dispatcher_(dispatcher),
run_depth_(0),
run_called_(false),
quit_called_(false),
running_(false),
- quit_when_idle_received_(false) {
+ quit_when_idle_received_(false),
+ weak_factory_(this) {
}
#endif
diff --git a/chromium/base/run_loop.h b/chromium/base/run_loop.h
index b1679073a11..055b1b8146d 100644
--- a/chromium/base/run_loop.h
+++ b/chromium/base/run_loop.h
@@ -97,9 +97,6 @@ class BASE_EXPORT RunLoop {
MessageLoop* loop_;
- // WeakPtrFactory for QuitClosure safety.
- base::WeakPtrFactory<RunLoop> weak_factory_;
-
// Parent RunLoop or NULL if this is the top-most RunLoop.
RunLoop* previous_run_loop_;
@@ -119,6 +116,9 @@ class BASE_EXPORT RunLoop {
// that we should quit Run once it becomes idle.
bool quit_when_idle_received_;
+ // WeakPtrFactory for QuitClosure safety.
+ base::WeakPtrFactory<RunLoop> weak_factory_;
+
DISALLOW_COPY_AND_ASSIGN(RunLoop);
};
diff --git a/chromium/base/scoped_native_library_unittest.cc b/chromium/base/scoped_native_library_unittest.cc
index c120555ec69..035faa03f74 100644
--- a/chromium/base/scoped_native_library_unittest.cc
+++ b/chromium/base/scoped_native_library_unittest.cc
@@ -18,19 +18,25 @@ TEST(ScopedNativeLibrary, Basic) {
// Get the pointer to DirectDrawCreate() from "ddraw.dll" and verify it
// is valid only in this scope.
// FreeLibrary() doesn't actually unload a DLL until its reference count
- // becomes zero, i.e. this function pointer is still valid if the DLL used
+ // becomes zero, i.e. function pointer is still valid if the DLL used
// in this test is also used by another part of this executable.
// So, this test uses "ddraw.dll", which is not used by Chrome at all but
// installed on all versions of Windows.
- FARPROC test_function;
+ const char kFunctionName[] = "DirectDrawCreate";
+ NativeLibrary native_library;
{
FilePath path(GetNativeLibraryName(L"ddraw"));
- ScopedNativeLibrary library(path);
- test_function = reinterpret_cast<FARPROC>(
- library.GetFunctionPointer("DirectDrawCreate"));
+ native_library = LoadNativeLibrary(path, NULL);
+ ScopedNativeLibrary library(native_library);
+ FARPROC test_function =
+ reinterpret_cast<FARPROC>(library.GetFunctionPointer(kFunctionName));
EXPECT_EQ(0, IsBadCodePtr(test_function));
+ EXPECT_EQ(
+ GetFunctionPointerFromNativeLibrary(native_library, kFunctionName),
+ test_function);
}
- EXPECT_NE(0, IsBadCodePtr(test_function));
+ EXPECT_EQ(NULL,
+ GetFunctionPointerFromNativeLibrary(native_library, kFunctionName));
#endif
}
diff --git a/chromium/base/security_unittest.cc b/chromium/base/security_unittest.cc
index 5b266b0a11c..cf3b1296667 100644
--- a/chromium/base/security_unittest.cc
+++ b/chromium/base/security_unittest.cc
@@ -75,14 +75,11 @@ bool IsTcMallocBypassed() {
}
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.
-// This function is explicitly disabled if we compile with AddressSanitizer,
-// MemorySanitizer or ThreadSanitizer.
-#if defined(OS_LINUX) && defined(NO_TCMALLOC) && \
- (!defined(ADDRESS_SANITIZER) && \
- !defined(MEMORY_SANITIZER) && \
- !defined(THREAD_SANITIZER))
+#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
+ defined(THREAD_SANITIZER) || (defined(OS_LINUX) && defined(NO_TCMALLOC))
return true;
#else
return false;
@@ -150,10 +147,10 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) {
// The tests bellow check for overflows in new[] and calloc().
-#if defined(OS_IOS) || defined(OS_WIN)
- #define DISABLE_ON_IOS_AND_WIN(function) DISABLED_##function
+#if defined(OS_IOS) || defined(OS_WIN) || defined(THREAD_SANITIZER)
+ #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) DISABLED_##function
#else
- #define DISABLE_ON_IOS_AND_WIN(function) function
+ #define DISABLE_ON_IOS_AND_WIN_AND_TSAN(function) function
#endif
// There are platforms where these tests are known to fail. We would like to
@@ -177,7 +174,7 @@ void OverflowTestsSoftExpectTrue(bool overflow_detected) {
// Test array[TooBig][X] and array[X][TooBig] allocations for int overflows.
// IOS doesn't honor nothrow, so disable the test there.
// Crashes on Windows Dbg builds, disable there as well.
-TEST(SecurityTest, DISABLE_ON_IOS_AND_WIN(NewOverflow)) {
+TEST(SecurityTest, DISABLE_ON_IOS_AND_WIN_AND_TSAN(NewOverflow)) {
const size_t kArraySize = 4096;
// We want something "dynamic" here, so that the compiler doesn't
// immediately reject crazy arrays.
@@ -233,19 +230,6 @@ TEST(SecurityTest, CallocOverflow) {
}
#if (defined(OS_LINUX) || defined(OS_CHROMEOS)) && defined(__x86_64__)
-// Useful for debugging.
-void PrintProcSelfMaps() {
- int fd = open("/proc/self/maps", O_RDONLY);
- file_util::ScopedFD fd_closer(&fd);
- ASSERT_GE(fd, 0);
- char buffer[1<<13];
- int ret;
- ret = read(fd, buffer, sizeof(buffer) - 1);
- ASSERT_GT(ret, 0);
- buffer[ret - 1] = 0;
- fprintf(stdout, "%s\n", buffer);
-}
-
// 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)) -
diff --git a/chromium/base/sequence_checker.h b/chromium/base/sequence_checker.h
index 89bbd7eca92..40d535e12d2 100644
--- a/chromium/base/sequence_checker.h
+++ b/chromium/base/sequence_checker.h
@@ -44,7 +44,7 @@ class SequenceCheckerDoNothing {
// class MyClass {
// public:
// void Foo() {
-// DCHECK(sequence_checker_.CalledOnValidSequence());
+// DCHECK(sequence_checker_.CalledOnValidSequencedThread());
// ... (do stuff) ...
// }
//
diff --git a/chromium/base/strings/safe_sprintf.cc b/chromium/base/strings/safe_sprintf.cc
index 2d4d93c947b..1e09b6e899e 100644
--- a/chromium/base/strings/safe_sprintf.cc
+++ b/chromium/base/strings/safe_sprintf.cc
@@ -107,12 +107,13 @@ class Buffer {
: buffer_(buffer),
size_(size - 1), // Account for trailing NUL byte
count_(0) {
-// This test should work on all C++11 compilers, but apparently something is
-// not working on all versions of clang just yet (e.g. on Mac, IOS, and
-// Android). We are conservative and exclude all of clang for the time being.
-// TODO(markus): Check if this restriction can be lifted.
-#if __cplusplus >= 201103 && !defined(__clang__)
- COMPILE_ASSERT(kSSizeMaxConst == std::numeric_limits<ssize_t>::max(),
+// The following assertion does not build on Mac and Android. This is because
+// static_assert only works with compile-time constants, but mac uses
+// libstdc++4.2 and android uses stlport, which both don't mark
+// numeric_limits::max() as constexp.
+#if __cplusplus >= 201103 && !defined(OS_ANDROID) && !defined(OS_MACOSX) && !defined(OS_IOS)
+ COMPILE_ASSERT(kSSizeMaxConst == \
+ static_cast<size_t>(std::numeric_limits<ssize_t>::max()),
kSSizeMax_is_the_max_value_of_an_ssize_t);
#endif
DEBUG_CHECK(size > 0);
diff --git a/chromium/base/strings/safe_sprintf.h b/chromium/base/strings/safe_sprintf.h
index a1be047955c..c9ee7173753 100644
--- a/chromium/base/strings/safe_sprintf.h
+++ b/chromium/base/strings/safe_sprintf.h
@@ -104,8 +104,6 @@ typedef long ssize_t;
// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for
// the use of RAW_CHECK() in debug builds, though.
//
-// The pre-C++11 version cannot handle more than ten arguments.
-//
// Basic example:
// char buf[20];
// base::strings::SafeSPrintf(buf, "The answer: %2d", 42);
@@ -190,29 +188,8 @@ BASE_EXPORT size_t GetSafeSPrintfSSizeMaxForTest();
} // namespace internal
-#if __cplusplus >= 201103 // C++11
-
-template<typename... Args>
-ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) {
- // Use Arg() object to record type information and then copy arguments to an
- // array to make it easier to iterate over them.
- const internal::Arg arg_array[] = { args... };
- return internal::SafeSNPrintf(buf, N, fmt, arg_array, arraysize(arg_array));
-}
-
-template<size_t N, typename... Args>
-ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) {
- // Use Arg() object to record type information and then copy arguments to an
- // array to make it easier to iterate over them.
- const internal::Arg arg_array[] = { args... };
- return internal::SafeSNPrintf(buf, N, fmt, arg_array, arraysize(arg_array));
-}
-
-#else // Pre-C++11
-
// TODO(markus): C++11 has a much more concise and readable solution for
-// expressing what we are doing here. Delete the fall-back code for older
-// compilers as soon as we have fully switched to C++11.
+// expressing what we are doing here.
template<class T0, class T1, class T2, class T3, class T4,
class T5, class T6, class T7, class T8, class T9>
@@ -426,7 +403,6 @@ ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, T0 arg0) {
const internal::Arg arg_array[] = { arg0 };
return internal::SafeSNPrintf(buf, N, fmt, arg_array, arraysize(arg_array));
}
-#endif
// Fast-path when we don't actually need to substitute any arguments.
BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt);
diff --git a/chromium/base/strings/safe_sprintf_unittest.cc b/chromium/base/strings/safe_sprintf_unittest.cc
index 0e142351103..937fa4eca23 100644
--- a/chromium/base/strings/safe_sprintf_unittest.cc
+++ b/chromium/base/strings/safe_sprintf_unittest.cc
@@ -264,18 +264,6 @@ TEST(SafeSPrintfTest, NArgs) {
EXPECT_EQ(10, SafeSNPrintf(buf, 11, "%c%c%c%c%c%c%c%c%c%c",
1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12", std::string(buf));
-
-
- // C++11 is smart enough to handle variadic template arguments. It can
- // deal with arbitrary numbers of arguments.
-#if __cplusplus >= 201103 // C++11
- EXPECT_EQ(11, SafeSPrintf(buf, "%c%c%c%c%c%c%c%c%c%c%c",
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
- EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
- EXPECT_EQ(11, SafeSNPrintf(buf, 12, "%c%c%c%c%c%c%c%c%c%c%c",
- 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
- EXPECT_EQ("\1\2\3\4\5\6\7\10\11\12\13", std::string(buf));
-#endif
}
TEST(SafeSPrintfTest, DataTypes) {
diff --git a/chromium/base/strings/string16.cc b/chromium/base/strings/string16.cc
index c802eef128f..f4c8cf74607 100644
--- a/chromium/base/strings/string16.cc
+++ b/chromium/base/strings/string16.cc
@@ -77,6 +77,6 @@ void PrintTo(const string16& str, std::ostream* out) {
} // namespace base
-template class std::basic_string<char16, base::string16_char_traits>;
+template class std::basic_string<base::char16, base::string16_char_traits>;
#endif // WCHAR_T_IS_UTF32
diff --git a/chromium/base/strings/string16_unittest.cc b/chromium/base/strings/string16_unittest.cc
index d98b2a9ec5d..4e582181a86 100644
--- a/chromium/base/strings/string16_unittest.cc
+++ b/chromium/base/strings/string16_unittest.cc
@@ -9,6 +9,8 @@
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
+namespace base {
+
#if defined(WCHAR_T_IS_UTF32)
// We define a custom operator<< for string16 so we can use it with logging.
@@ -52,3 +54,5 @@ TEST(String16Test, OutputStream) {
}
#endif
+
+} // namespace base
diff --git a/chromium/base/strings/string_number_conversions.cc b/chromium/base/strings/string_number_conversions.cc
index b9412d93131..e3e71bef023 100644
--- a/chromium/base/strings/string_number_conversions.cc
+++ b/chromium/base/strings/string_number_conversions.cc
@@ -301,6 +301,11 @@ class BaseHexIteratorRangeToIntTraits
};
template<typename ITERATOR>
+class BaseHexIteratorRangeToUIntTraits
+ : public BaseIteratorRangeToNumberTraits<ITERATOR, uint32, 16> {
+};
+
+template<typename ITERATOR>
class BaseHexIteratorRangeToInt64Traits
: public BaseIteratorRangeToNumberTraits<ITERATOR, int64, 16> {
};
@@ -313,6 +318,9 @@ class BaseHexIteratorRangeToUInt64Traits
typedef BaseHexIteratorRangeToIntTraits<StringPiece::const_iterator>
HexIteratorRangeToIntTraits;
+typedef BaseHexIteratorRangeToUIntTraits<StringPiece::const_iterator>
+ HexIteratorRangeToUIntTraits;
+
typedef BaseHexIteratorRangeToInt64Traits<StringPiece::const_iterator>
HexIteratorRangeToInt64Traits;
@@ -499,6 +507,11 @@ bool HexStringToInt(const StringPiece& input, int* output) {
input.begin(), input.end(), output);
}
+bool HexStringToUInt(const StringPiece& input, uint32* output) {
+ return IteratorRangeToNumber<HexIteratorRangeToUIntTraits>::Invoke(
+ input.begin(), input.end(), output);
+}
+
bool HexStringToInt64(const StringPiece& input, int64* output) {
return IteratorRangeToNumber<HexIteratorRangeToInt64Traits>::Invoke(
input.begin(), input.end(), output);
diff --git a/chromium/base/strings/string_number_conversions.h b/chromium/base/strings/string_number_conversions.h
index b199bb5f7a9..6f5df4a093f 100644
--- a/chromium/base/strings/string_number_conversions.h
+++ b/chromium/base/strings/string_number_conversions.h
@@ -101,6 +101,12 @@ BASE_EXPORT bool HexStringToInt(const StringPiece& input, int* output);
// Best effort conversion, see StringToInt above for restrictions.
// Will only successful parse hex values that will fit into |output|, i.e.
+// 0x00000000 < |input| < 0xFFFFFFFF.
+// The string is not required to start with 0x.
+BASE_EXPORT bool HexStringToUInt(const StringPiece& input, uint32* output);
+
+// Best effort conversion, see StringToInt above for restrictions.
+// Will only successful parse hex values that will fit into |output|, i.e.
// -0x8000000000000000 < |input| < 0x7FFFFFFFFFFFFFFF.
BASE_EXPORT bool HexStringToInt64(const StringPiece& input, int64* output);
diff --git a/chromium/base/strings/string_number_conversions_unittest.cc b/chromium/base/strings/string_number_conversions_unittest.cc
index b8619647de5..b6e5f961ce2 100644
--- a/chromium/base/strings/string_number_conversions_unittest.cc
+++ b/chromium/base/strings/string_number_conversions_unittest.cc
@@ -458,6 +458,66 @@ TEST(StringNumberConversionsTest, HexStringToInt) {
EXPECT_EQ(0xc0ffee, output);
}
+TEST(StringNumberConversionsTest, HexStringToUInt) {
+ static const struct {
+ std::string input;
+ uint32 output;
+ bool success;
+ } cases[] = {
+ {"0", 0, true},
+ {"42", 0x42, true},
+ {"-42", 0, false},
+ {"+42", 0x42, true},
+ {"7fffffff", INT_MAX, true},
+ {"-80000000", 0, false},
+ {"ffffffff", 0xffffffff, true},
+ {"DeadBeef", 0xdeadbeef, true},
+ {"0x42", 0x42, true},
+ {"-0x42", 0, false},
+ {"+0x42", 0x42, true},
+ {"0x7fffffff", INT_MAX, true},
+ {"-0x80000000", 0, false},
+ {"0xffffffff", kuint32max, true},
+ {"0XDeadBeef", 0xdeadbeef, true},
+ {"0x7fffffffffffffff", kuint32max, false}, // Overflow test.
+ {"-0x8000000000000000", 0, false},
+ {"0x8000000000000000", kuint32max, false}, // Overflow test.
+ {"-0x8000000000000001", 0, false},
+ {"0xFFFFFFFFFFFFFFFF", kuint32max, false}, // Overflow test.
+ {"FFFFFFFFFFFFFFFF", kuint32max, false}, // Overflow test.
+ {"0x0000000000000000", 0, true},
+ {"0000000000000000", 0, true},
+ {"1FFFFFFFFFFFFFFFF", kuint32max, false}, // Overflow test.
+ {"0x0f", 0x0f, true},
+ {"0f", 0x0f, true},
+ {" 45", 0x45, false},
+ {"\t\n\v\f\r 0x45", 0x45, false},
+ {" 45", 0x45, false},
+ {"45 ", 0x45, false},
+ {"45:", 0x45, false},
+ {"efgh", 0xef, false},
+ {"0xefgh", 0xef, false},
+ {"hgfe", 0, false},
+ {"-", 0, false},
+ {"", 0, false},
+ {"0x", 0, false},
+ };
+
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
+ uint32 output = 0;
+ EXPECT_EQ(cases[i].success, HexStringToUInt(cases[i].input, &output));
+ EXPECT_EQ(cases[i].output, output);
+ }
+ // One additional test to verify that conversion of numbers in strings with
+ // embedded NUL characters. The NUL and extra data after it should be
+ // interpreted as junk after the number.
+ const char input[] = "0xc0ffee\09";
+ std::string input_string(input, arraysize(input) - 1);
+ uint32 output;
+ EXPECT_FALSE(HexStringToUInt(input_string, &output));
+ EXPECT_EQ(0xc0ffeeU, output);
+}
+
TEST(StringNumberConversionsTest, HexStringToInt64) {
static const struct {
std::string input;
diff --git a/chromium/base/strings/string_split.h b/chromium/base/strings/string_split.h
index faf08d6985d..7c27e4899da 100644
--- a/chromium/base/strings/string_split.h
+++ b/chromium/base/strings/string_split.h
@@ -14,9 +14,9 @@
namespace base {
-// Splits |str| into a vector of strings delimited by |s|, placing the results
-// in |r|. If several instances of |s| are contiguous, or if |str| begins with
-// or ends with |s|, then an empty string is inserted.
+// Splits |str| into a vector of strings delimited by |c|, placing the results
+// in |r|. If several instances of |c| are contiguous, or if |str| begins with
+// or ends with |c|, then an empty string is inserted.
//
// Every substring is trimmed of any leading or trailing white space.
// NOTE: |c| must be in BMP (Basic Multilingual Plane)
diff --git a/chromium/base/strings/string_util.cc b/chromium/base/strings/string_util.cc
index 3ed706978e2..cee124b0c1a 100644
--- a/chromium/base/strings/string_util.cc
+++ b/chromium/base/strings/string_util.cc
@@ -26,6 +26,10 @@
#include "base/third_party/icu/icu_utf.h"
#include "build/build_config.h"
+// Remove when this entire file is in the base namespace.
+using base::char16;
+using base::string16;
+
namespace {
// Force the singleton used by Empty[W]String[16] to be a unique type. This
@@ -100,9 +104,6 @@ bool IsWprintfFormatPortable(const wchar_t* format) {
return true;
}
-} // namespace base
-
-
const std::string& EmptyString() {
return EmptyStrings::GetInstance()->s;
}
@@ -193,19 +194,11 @@ TrimPositions TrimStringT(const STR& input,
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
}
-bool TrimString(const std::wstring& input,
- const wchar_t trim_chars[],
- std::wstring* output) {
- return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool TrimString(const string16& input,
const char16 trim_chars[],
string16* output) {
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
}
-#endif
bool TrimString(const std::string& input,
const char trim_chars[],
@@ -235,8 +228,8 @@ void TruncateUTF8ToByteSize(const std::string& input,
int32 prev = char_index;
uint32 code_point = 0;
CBU8_NEXT(data, char_index, truncation_length, code_point);
- if (!base::IsValidCharacter(code_point) ||
- !base::IsValidCodepoint(code_point)) {
+ if (!IsValidCharacter(code_point) ||
+ !IsValidCodepoint(code_point)) {
char_index = prev - 1;
} else {
break;
@@ -249,16 +242,18 @@ void TruncateUTF8ToByteSize(const std::string& input,
output->clear();
}
-TrimPositions TrimWhitespace(const string16& input,
+} // namespace base
+
+TrimPositions TrimWhitespace(const base::string16& input,
TrimPositions positions,
- string16* output) {
- return TrimStringT(input, kWhitespaceUTF16, positions, output);
+ base::string16* output) {
+ return base::TrimStringT(input, base::kWhitespaceUTF16, positions, output);
}
TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output) {
- return TrimStringT(input, kWhitespaceASCII, positions, output);
+ return base::TrimStringT(input, base::kWhitespaceASCII, positions, output);
}
// This function is only for backward-compatibility.
@@ -311,17 +306,10 @@ STR CollapseWhitespaceT(const STR& text,
return result;
}
-std::wstring CollapseWhitespace(const std::wstring& text,
- bool trim_sequences_with_line_breaks) {
- return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
string16 CollapseWhitespace(const string16& text,
bool trim_sequences_with_line_breaks) {
return CollapseWhitespaceT(text, trim_sequences_with_line_breaks);
}
-#endif
std::string CollapseWhitespaceASCII(const std::string& text,
bool trim_sequences_with_line_breaks) {
@@ -336,8 +324,8 @@ bool ContainsOnlyWhitespaceASCII(const std::string& str) {
return true;
}
-bool ContainsOnlyWhitespace(const string16& str) {
- return str.find_first_not_of(kWhitespaceUTF16) == string16::npos;
+bool ContainsOnlyWhitespace(const base::string16& str) {
+ return str.find_first_not_of(base::kWhitespaceUTF16) == string16::npos;
}
template<typename STR>
@@ -350,22 +338,19 @@ static bool ContainsOnlyCharsT(const STR& input, const STR& characters) {
return true;
}
-bool ContainsOnlyChars(const std::wstring& input,
- const std::wstring& characters) {
- return ContainsOnlyCharsT(input, characters);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool ContainsOnlyChars(const string16& input, const string16& characters) {
return ContainsOnlyCharsT(input, characters);
}
-#endif
bool ContainsOnlyChars(const std::string& input,
const std::string& characters) {
return ContainsOnlyCharsT(input, characters);
}
+#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());
@@ -376,20 +361,6 @@ std::string UTF16ToASCII(const string16& utf16) {
return std::string(utf16.begin(), utf16.end());
}
-// Latin1 is just the low range of Unicode, so we can copy directly to convert.
-bool WideToLatin1(const std::wstring& wide, std::string* latin1) {
- std::string output;
- output.resize(wide.size());
- latin1->clear();
- for (size_t i = 0; i < wide.size(); i++) {
- if (wide[i] > 255)
- return false;
- output[i] = static_cast<char>(wide[i]);
- }
- latin1->swap(output);
- return true;
-}
-
template<class STR>
static bool DoIsStringASCII(const STR& str) {
for (size_t i = 0; i < str.length(); i++) {
@@ -400,15 +371,15 @@ static bool DoIsStringASCII(const STR& str) {
return true;
}
+#if !defined(WCHAR_T_IS_UTF16)
bool IsStringASCII(const std::wstring& str) {
return DoIsStringASCII(str);
}
+#endif
-#if !defined(WCHAR_T_IS_UTF16)
bool IsStringASCII(const string16& str) {
return DoIsStringASCII(str);
}
-#endif
bool IsStringASCII(const base::StringPiece& str) {
return DoIsStringASCII(str);
@@ -444,15 +415,9 @@ bool LowerCaseEqualsASCII(const std::string& a, const char* b) {
return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
}
-bool LowerCaseEqualsASCII(const std::wstring& a, const char* b) {
- return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool LowerCaseEqualsASCII(const string16& a, const char* b) {
return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
}
-#endif
bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
std::string::const_iterator a_end,
@@ -460,19 +425,11 @@ bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
return DoLowerCaseEqualsASCII(a_begin, a_end, b);
}
-bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
- std::wstring::const_iterator a_end,
- const char* b) {
- return DoLowerCaseEqualsASCII(a_begin, a_end, b);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool LowerCaseEqualsASCII(string16::const_iterator a_begin,
string16::const_iterator a_end,
const char* b) {
return DoLowerCaseEqualsASCII(a_begin, a_end, b);
}
-#endif
// TODO(port): Resolve wchar_t/iterator issues that require OS_ANDROID here.
#if !defined(OS_ANDROID)
@@ -482,19 +439,11 @@ bool LowerCaseEqualsASCII(const char* a_begin,
return DoLowerCaseEqualsASCII(a_begin, a_end, b);
}
-bool LowerCaseEqualsASCII(const wchar_t* a_begin,
- const wchar_t* a_end,
- const char* b) {
- return DoLowerCaseEqualsASCII(a_begin, a_end, b);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool LowerCaseEqualsASCII(const char16* a_begin,
const char16* a_end,
const char* b) {
return DoLowerCaseEqualsASCII(a_begin, a_end, b);
}
-#endif
#endif // !defined(OS_ANDROID)
@@ -525,17 +474,10 @@ bool StartsWithT(const STR& str, const STR& search, bool case_sensitive) {
}
}
-bool StartsWith(const std::wstring& str, const std::wstring& search,
- bool case_sensitive) {
- return StartsWithT(str, search, case_sensitive);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool StartsWith(const string16& str, const string16& search,
bool case_sensitive) {
return StartsWithT(str, search, case_sensitive);
}
-#endif
template <typename STR>
bool EndsWithT(const STR& str, const STR& search, bool case_sensitive) {
@@ -557,17 +499,10 @@ bool EndsWith(const std::string& str, const std::string& search,
return EndsWithT(str, search, case_sensitive);
}
-bool EndsWith(const std::wstring& str, const std::wstring& search,
- bool case_sensitive) {
- return EndsWithT(str, search, case_sensitive);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
bool EndsWith(const string16& str, const string16& search,
bool case_sensitive) {
return EndsWithT(str, search, case_sensitive);
}
-#endif
static const char* const kByteStringsUnlocalized[] = {
" B",
@@ -674,19 +609,11 @@ static size_t TokenizeT(const STR& str,
return tokens->size();
}
-size_t Tokenize(const std::wstring& str,
- const std::wstring& delimiters,
- std::vector<std::wstring>* tokens) {
- return TokenizeT(str, delimiters, tokens);
-}
-
-#if !defined(WCHAR_T_IS_UTF16)
size_t Tokenize(const string16& str,
const string16& delimiters,
std::vector<string16>* tokens) {
return TokenizeT(str, delimiters, tokens);
}
-#endif
size_t Tokenize(const std::string& str,
const std::string& delimiters,
@@ -817,10 +744,9 @@ string16 ReplaceStringPlaceholders(const string16& format_string,
subst.push_back(a);
string16 result = ReplaceStringPlaceholders(format_string, subst, &offsets);
- DCHECK(offsets.size() == 1);
- if (offset) {
+ DCHECK_EQ(1U, offsets.size());
+ if (offset)
*offset = offsets[0];
- }
return result;
}
diff --git a/chromium/base/strings/string_util.h b/chromium/base/strings/string_util.h
index d2a216efaf6..7b4b2193abf 100644
--- a/chromium/base/strings/string_util.h
+++ b/chromium/base/strings/string_util.h
@@ -47,14 +47,6 @@ int strncmp16(const char16* s1, const char16* s2, size_t count);
int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments)
PRINTF_FORMAT(3, 0);
-// vswprintf always null-terminates, but when truncation occurs, it will either
-// return -1 or the number of characters that would be in an untruncated
-// formatted string. The actual return value depends on the underlying
-// C library's vswprintf implementation.
-int vswprintf(wchar_t* buffer, size_t size,
- const wchar_t* format, va_list arguments)
- WPRINTF_FORMAT(3, 0);
-
// Some of these implementations need to be inlined.
// We separate the declaration from the implementation of this inline
@@ -69,18 +61,6 @@ inline int snprintf(char* buffer, size_t size, const char* format, ...) {
return result;
}
-// We separate the declaration from the implementation of this inline
-// function just so the WPRINTF_FORMAT works.
-inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...)
- WPRINTF_FORMAT(3, 4);
-inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...) {
- va_list arguments;
- va_start(arguments, format);
- int result = vswprintf(buffer, size, format, arguments);
- va_end(arguments);
- return result;
-}
-
// BSD-style safe and consistent string copy functions.
// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|.
// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as
@@ -143,33 +123,29 @@ template<typename Char> struct CaseInsensitiveCompareASCII {
}
};
-} // 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
-
// These threadsafe functions return references to globally unique empty
// strings.
//
-// DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT CONSTRUCTORS.
-// There is only one case where you should use these: functions which need to
-// return a string by reference (e.g. as a class member accessor), and don't
-// have an empty string to use (e.g. in an error case). These should not be
-// used as initializers, function arguments, or return values for functions
-// which return by value or outparam.
+// It is likely faster to construct a new empty string object (just a few
+// instructions to set the length to 0) than to get the empty string singleton
+// returned by these functions (which requires threadsafe singleton access).
+//
+// Therefore, DO NOT USE THESE AS A GENERAL-PURPOSE SUBSTITUTE FOR DEFAULT
+// CONSTRUCTORS. There is only one case where you should use these: functions
+// which need to return a string by reference (e.g. as a class member
+// accessor), and don't have an empty string to use (e.g. in an error case).
+// These should not be used as initializers, function arguments, or return
+// values for functions which return by value or outparam.
BASE_EXPORT const std::string& EmptyString();
-BASE_EXPORT const std::wstring& EmptyWString();
BASE_EXPORT const string16& EmptyString16();
+// Contains the set of characters representing whitespace in the corresponding
+// encoding. Null-terminated.
BASE_EXPORT extern const wchar_t kWhitespaceWide[];
BASE_EXPORT extern const char16 kWhitespaceUTF16[];
BASE_EXPORT extern const char kWhitespaceASCII[];
+// Null-terminated string representing the UTF-8 byte order mark.
BASE_EXPORT extern const char kUtf8ByteOrderMark[];
// Removes characters in |remove_chars| from anywhere in |input|. Returns true
@@ -199,9 +175,6 @@ BASE_EXPORT bool ReplaceChars(const std::string& input,
// Removes characters in |trim_chars| from the beginning and end of |input|.
// |trim_chars| must be null-terminated.
// NOTE: Safe to use the same variable for both |input| and |output|.
-BASE_EXPORT bool TrimString(const std::wstring& input,
- const wchar_t trim_chars[],
- std::wstring* output);
BASE_EXPORT bool TrimString(const string16& input,
const char16 trim_chars[],
string16* output);
@@ -215,6 +188,16 @@ 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:
@@ -228,9 +211,9 @@ enum TrimPositions {
TRIM_TRAILING = 1 << 1,
TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
};
-BASE_EXPORT TrimPositions TrimWhitespace(const string16& input,
+BASE_EXPORT TrimPositions TrimWhitespace(const base::string16& input,
TrimPositions positions,
- string16* output);
+ base::string16* output);
BASE_EXPORT TrimPositions TrimWhitespaceASCII(const std::string& input,
TrimPositions positions,
std::string* output);
@@ -249,11 +232,8 @@ 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 std::wstring CollapseWhitespace(
- const std::wstring& text,
- bool trim_sequences_with_line_breaks);
-BASE_EXPORT string16 CollapseWhitespace(
- const string16& text,
+BASE_EXPORT base::string16 CollapseWhitespace(
+ const base::string16& text,
bool trim_sequences_with_line_breaks);
BASE_EXPORT std::string CollapseWhitespaceASCII(
const std::string& text,
@@ -262,25 +242,19 @@ BASE_EXPORT std::string CollapseWhitespaceASCII(
// Returns true if the passed string is empty or contains only white-space
// characters.
BASE_EXPORT bool ContainsOnlyWhitespaceASCII(const std::string& str);
-BASE_EXPORT bool ContainsOnlyWhitespace(const string16& str);
+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 std::wstring& input,
- const std::wstring& characters);
-BASE_EXPORT bool ContainsOnlyChars(const string16& input,
- const string16& 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 string16& utf16);
-
-// Converts the given wide string to the corresponding Latin1. This will fail
-// (return false) if any characters are more than 255.
-BASE_EXPORT bool WideToLatin1(const std::wstring& wide, std::string* latin1);
+BASE_EXPORT std::string UTF16ToASCII(const base::string16& utf16);
// 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
@@ -294,9 +268,8 @@ BASE_EXPORT bool WideToLatin1(const std::wstring& wide, std::string* latin1);
// there's a use case for just checking the structural validity, we have to
// add a new function for that.
BASE_EXPORT bool IsStringUTF8(const std::string& str);
-BASE_EXPORT bool IsStringASCII(const std::wstring& str);
BASE_EXPORT bool IsStringASCII(const base::StringPiece& str);
-BASE_EXPORT bool IsStringASCII(const string16& str);
+BASE_EXPORT bool IsStringASCII(const base::string16& str);
// Converts the elements of the given string. This version uses a pointer to
// clearly differentiate it from the non-pointer variant.
@@ -331,53 +304,40 @@ template <class str> inline str StringToUpperASCII(const str& s) {
// token, and it is optimized to avoid intermediate string copies. This API is
// borrowed from the equivalent APIs in Mozilla.
BASE_EXPORT bool LowerCaseEqualsASCII(const std::string& a, const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(const std::wstring& a, const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(const string16& a, const char* b);
+BASE_EXPORT bool LowerCaseEqualsASCII(const base::string16& a, const char* b);
// Same thing, but with string iterators instead.
BASE_EXPORT bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
std::string::const_iterator a_end,
const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
- std::wstring::const_iterator a_end,
- const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(string16::const_iterator a_begin,
- string16::const_iterator a_end,
+BASE_EXPORT bool LowerCaseEqualsASCII(base::string16::const_iterator a_begin,
+ base::string16::const_iterator a_end,
const char* b);
BASE_EXPORT bool LowerCaseEqualsASCII(const char* a_begin,
const char* a_end,
const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(const wchar_t* a_begin,
- const wchar_t* a_end,
- const char* b);
-BASE_EXPORT bool LowerCaseEqualsASCII(const char16* a_begin,
- const char16* a_end,
+BASE_EXPORT bool LowerCaseEqualsASCII(const base::char16* a_begin,
+ const base::char16* a_end,
const char* b);
// Performs a case-sensitive string compare. The behavior is undefined if both
// strings are not ASCII.
-BASE_EXPORT bool EqualsASCII(const string16& a, const base::StringPiece& b);
+BASE_EXPORT bool EqualsASCII(const base::string16& a, const base::StringPiece& b);
// Returns true if str starts with search, or false otherwise.
BASE_EXPORT bool StartsWithASCII(const std::string& str,
const std::string& search,
bool case_sensitive);
-BASE_EXPORT bool StartsWith(const std::wstring& str,
- const std::wstring& search,
- bool case_sensitive);
-BASE_EXPORT bool StartsWith(const string16& str,
- const string16& search,
+BASE_EXPORT bool StartsWith(const base::string16& str,
+ const base::string16& search,
bool case_sensitive);
// Returns true if str ends with search, or false otherwise.
BASE_EXPORT bool EndsWith(const std::string& str,
const std::string& search,
bool case_sensitive);
-BASE_EXPORT bool EndsWith(const std::wstring& str,
- const std::wstring& search,
- bool case_sensitive);
-BASE_EXPORT bool EndsWith(const string16& str,
- const string16& search,
+BASE_EXPORT bool EndsWith(const base::string16& str,
+ const base::string16& search,
bool case_sensitive);
@@ -417,22 +377,22 @@ inline Char HexDigitToInt(Char c) {
// Returns true if it's a whitespace character.
inline bool IsWhitespace(wchar_t c) {
- return wcschr(kWhitespaceWide, c) != NULL;
+ return wcschr(base::kWhitespaceWide, c) != NULL;
}
// Return a byte string in human-readable format with a unit suffix. Not
// appropriate for use in any UI; use of FormatBytes and friends in ui/base is
// highly recommended instead. TODO(avi): Figure out how to get callers to use
// FormatBytes instead; remove this.
-BASE_EXPORT string16 FormatBytesUnlocalized(int64 bytes);
+BASE_EXPORT base::string16 FormatBytesUnlocalized(int64 bytes);
// Starting at |start_offset| (usually 0), replace the first instance of
// |find_this| with |replace_with|.
BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
- string16* str,
- string16::size_type start_offset,
- const string16& find_this,
- const string16& replace_with);
+ base::string16* str,
+ base::string16::size_type 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,
@@ -446,10 +406,10 @@ BASE_EXPORT void ReplaceFirstSubstringAfterOffset(
// characters, for example:
// std::replace(str.begin(), str.end(), 'a', 'b');
BASE_EXPORT void ReplaceSubstringsAfterOffset(
- string16* str,
- string16::size_type start_offset,
- const string16& find_this,
- const string16& replace_with);
+ base::string16* str,
+ base::string16::size_type 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,
@@ -490,12 +450,9 @@ inline typename string_type::value_type* WriteInto(string_type* str,
// Splits a string into its fields delimited by any of the characters in
// |delimiters|. Each field is added to the |tokens| vector. Returns the
// number of tokens found.
-BASE_EXPORT size_t Tokenize(const std::wstring& str,
- const std::wstring& delimiters,
- std::vector<std::wstring>* tokens);
-BASE_EXPORT size_t Tokenize(const string16& str,
- const string16& delimiters,
- std::vector<string16>* tokens);
+BASE_EXPORT size_t Tokenize(const base::string16& str,
+ const base::string16& delimiters,
+ std::vector<base::string16>* tokens);
BASE_EXPORT size_t Tokenize(const std::string& str,
const std::string& delimiters,
std::vector<std::string>* tokens);
@@ -504,7 +461,8 @@ BASE_EXPORT size_t Tokenize(const base::StringPiece& str,
std::vector<base::StringPiece>* tokens);
// Does the opposite of SplitString().
-BASE_EXPORT string16 JoinString(const std::vector<string16>& parts, char16 s);
+BASE_EXPORT base::string16 JoinString(const std::vector<base::string16>& parts,
+ base::char16 s);
BASE_EXPORT std::string JoinString(
const std::vector<std::string>& parts, char s);
@@ -512,17 +470,17 @@ BASE_EXPORT std::string JoinString(
BASE_EXPORT std::string JoinString(
const std::vector<std::string>& parts,
const std::string& separator);
-BASE_EXPORT string16 JoinString(
- const std::vector<string16>& parts,
- const string16& separator);
+BASE_EXPORT base::string16 JoinString(
+ const std::vector<base::string16>& parts,
+ const base::string16& separator);
// Replace $1-$2-$3..$9 in the format string with |a|-|b|-|c|..|i| respectively.
// Additionally, any number of consecutive '$' characters is replaced by that
// number less one. Eg $$->$, $$$->$$, etc. The offsets parameter here can be
// NULL. This only allows you to use up to nine replacements.
-BASE_EXPORT string16 ReplaceStringPlaceholders(
- const string16& format_string,
- const std::vector<string16>& subst,
+BASE_EXPORT base::string16 ReplaceStringPlaceholders(
+ const base::string16& format_string,
+ const std::vector<base::string16>& subst,
std::vector<size_t>* offsets);
BASE_EXPORT std::string ReplaceStringPlaceholders(
@@ -531,9 +489,10 @@ BASE_EXPORT std::string ReplaceStringPlaceholders(
std::vector<size_t>* offsets);
// Single-string shortcut for ReplaceStringHolders. |offset| may be NULL.
-BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
- const string16& a,
- size_t* offset);
+BASE_EXPORT base::string16 ReplaceStringPlaceholders(
+ const base::string16& format_string,
+ const base::string16& a,
+ size_t* offset);
// Returns true if the string passed in matches the pattern. The pattern
// string can contain wildcards like * and ?
@@ -542,7 +501,8 @@ BASE_EXPORT string16 ReplaceStringPlaceholders(const string16& format_string,
// ? matches 0 or 1 character, while * matches 0 or more characters.
BASE_EXPORT bool MatchPattern(const base::StringPiece& string,
const base::StringPiece& pattern);
-BASE_EXPORT bool MatchPattern(const string16& string, const string16& pattern);
+BASE_EXPORT bool MatchPattern(const base::string16& string,
+ const base::string16& pattern);
// Hack to convert any char-like type to its unsigned counterpart.
// For example, it will convert char, signed char and unsigned char to unsigned
diff --git a/chromium/base/strings/string_util_constants.cc b/chromium/base/strings/string_util_constants.cc
index d92e40cf37b..2a28a2b5126 100644
--- a/chromium/base/strings/string_util_constants.cc
+++ b/chromium/base/strings/string_util_constants.cc
@@ -4,6 +4,8 @@
#include "base/strings/string_util.h"
+namespace base {
+
#define WHITESPACE_UNICODE \
0x0009, /* <control-0009> to <control-000D> */ \
0x000A, \
@@ -53,3 +55,5 @@ const char kWhitespaceASCII[] = {
};
const char kUtf8ByteOrderMark[] = "\xEF\xBB\xBF";
+
+} // namespace base
diff --git a/chromium/base/strings/string_util_unittest.cc b/chromium/base/strings/string_util_unittest.cc
index 58b7620b352..e743bb41223 100644
--- a/chromium/base/strings/string_util_unittest.cc
+++ b/chromium/base/strings/string_util_unittest.cc
@@ -7,8 +7,7 @@
#include <math.h>
#include <stdarg.h>
-#include <limits>
-#include <sstream>
+#include <algorithm>
#include "base/basictypes.h"
#include "base/strings/string16.h"
@@ -284,7 +283,8 @@ static const struct collapse_case {
TEST(StringUtilTest, CollapseWhitespace) {
for (size_t i = 0; i < arraysize(collapse_cases); ++i) {
const collapse_case& value = collapse_cases[i];
- EXPECT_EQ(value.output, CollapseWhitespace(value.input, value.trim));
+ EXPECT_EQ(WideToUTF16(value.output),
+ CollapseWhitespace(WideToUTF16(value.input), value.trim));
}
}
@@ -421,13 +421,11 @@ TEST(StringUtilTest, ConvertASCII) {
std::wstring wide = ASCIIToWide(char_cases[i]);
EXPECT_EQ(wchar_cases[i], wide);
- EXPECT_TRUE(IsStringASCII(wchar_cases[i]));
std::string ascii = WideToASCII(wchar_cases[i]);
EXPECT_EQ(char_cases[i], ascii);
}
EXPECT_FALSE(IsStringASCII("Google \x80Video"));
- EXPECT_FALSE(IsStringASCII(L"Google \x80Video"));
// Convert empty strings.
std::wstring wempty;
@@ -476,17 +474,16 @@ TEST(StringUtilTest, ToUpperASCII) {
TEST(StringUtilTest, LowerCaseEqualsASCII) {
static const struct {
- const wchar_t* src_w;
const char* src_a;
const char* dst;
} lowercase_cases[] = {
- { L"FoO", "FoO", "foo" },
- { L"foo", "foo", "foo" },
- { L"FOO", "FOO", "foo" },
+ { "FoO", "foo" },
+ { "foo", "foo" },
+ { "FOO", "foo" },
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(lowercase_cases); ++i) {
- EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_w,
+ EXPECT_TRUE(LowerCaseEqualsASCII(ASCIIToUTF16(lowercase_cases[i].src_a),
lowercase_cases[i].dst));
EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_a,
lowercase_cases[i].dst));
@@ -818,35 +815,48 @@ TEST(StringUtilTest, StartsWith) {
EXPECT_TRUE(StartsWithASCII("java", std::string(), false));
EXPECT_TRUE(StartsWithASCII("java", std::string(), true));
- EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", true));
- EXPECT_FALSE(StartsWith(L"JavaScript:url", L"javascript", true));
- EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", false));
- EXPECT_TRUE(StartsWith(L"JavaScript:url", L"javascript", false));
- EXPECT_FALSE(StartsWith(L"java", L"javascript", true));
- EXPECT_FALSE(StartsWith(L"java", L"javascript", false));
- EXPECT_FALSE(StartsWith(std::wstring(), L"javascript", false));
- EXPECT_FALSE(StartsWith(std::wstring(), L"javascript", true));
- EXPECT_TRUE(StartsWith(L"java", std::wstring(), false));
- EXPECT_TRUE(StartsWith(L"java", std::wstring(), true));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
+ ASCIIToUTF16("javascript"), true));
+ EXPECT_FALSE(StartsWith(ASCIIToUTF16("JavaScript:url"),
+ ASCIIToUTF16("javascript"), true));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("javascript:url"),
+ ASCIIToUTF16("javascript"), false));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("JavaScript:url"),
+ ASCIIToUTF16("javascript"), false));
+ EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"),
+ ASCIIToUTF16("javascript"), true));
+ EXPECT_FALSE(StartsWith(ASCIIToUTF16("java"),
+ ASCIIToUTF16("javascript"), false));
+ EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"), false));
+ EXPECT_FALSE(StartsWith(string16(), ASCIIToUTF16("javascript"), true));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(), false));
+ EXPECT_TRUE(StartsWith(ASCIIToUTF16("java"), string16(), true));
}
TEST(StringUtilTest, EndsWith) {
- EXPECT_TRUE(EndsWith(L"Foo.plugin", L".plugin", true));
- EXPECT_FALSE(EndsWith(L"Foo.Plugin", L".plugin", true));
- EXPECT_TRUE(EndsWith(L"Foo.plugin", L".plugin", false));
- EXPECT_TRUE(EndsWith(L"Foo.Plugin", L".plugin", false));
- EXPECT_FALSE(EndsWith(L".plug", L".plugin", true));
- EXPECT_FALSE(EndsWith(L".plug", L".plugin", false));
- EXPECT_FALSE(EndsWith(L"Foo.plugin Bar", L".plugin", true));
- EXPECT_FALSE(EndsWith(L"Foo.plugin Bar", L".plugin", false));
- EXPECT_FALSE(EndsWith(std::wstring(), L".plugin", false));
- EXPECT_FALSE(EndsWith(std::wstring(), L".plugin", true));
- EXPECT_TRUE(EndsWith(L"Foo.plugin", std::wstring(), false));
- EXPECT_TRUE(EndsWith(L"Foo.plugin", std::wstring(), true));
- EXPECT_TRUE(EndsWith(L".plugin", L".plugin", false));
- EXPECT_TRUE(EndsWith(L".plugin", L".plugin", true));
- EXPECT_TRUE(EndsWith(std::wstring(), std::wstring(), false));
- EXPECT_TRUE(EndsWith(std::wstring(), std::wstring(), true));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"),
+ ASCIIToUTF16(".plugin"), true));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.Plugin"),
+ ASCIIToUTF16(".plugin"), true));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"),
+ ASCIIToUTF16(".plugin"), false));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.Plugin"),
+ ASCIIToUTF16(".plugin"), false));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"), true));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16(".plug"), ASCIIToUTF16(".plugin"), false));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"),
+ ASCIIToUTF16(".plugin"), true));
+ EXPECT_FALSE(EndsWith(ASCIIToUTF16("Foo.plugin Bar"),
+ ASCIIToUTF16(".plugin"), false));
+ EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"), false));
+ EXPECT_FALSE(EndsWith(string16(), ASCIIToUTF16(".plugin"), true));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(), false));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16("Foo.plugin"), string16(), true));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"),
+ ASCIIToUTF16(".plugin"), false));
+ EXPECT_TRUE(EndsWith(ASCIIToUTF16(".plugin"), ASCIIToUTF16(".plugin"), true));
+ EXPECT_TRUE(EndsWith(string16(), string16(), false));
+ EXPECT_TRUE(EndsWith(string16(), string16(), true));
}
TEST(StringUtilTest, GetStringFWithOffsets) {
diff --git a/chromium/base/supports_user_data.h b/chromium/base/supports_user_data.h
index 77367553daf..6d515d7341e 100644
--- a/chromium/base/supports_user_data.h
+++ b/chromium/base/supports_user_data.h
@@ -61,7 +61,7 @@ class BASE_EXPORT SupportsUserData {
template <typename T>
class UserDataAdapter : public base::SupportsUserData::Data {
public:
- static T* Get(SupportsUserData* supports_user_data, const char* key) {
+ static T* Get(SupportsUserData* supports_user_data, const void* key) {
UserDataAdapter* data =
static_cast<UserDataAdapter*>(supports_user_data->GetUserData(key));
return data ? static_cast<T*>(data->object_.get()) : NULL;
diff --git a/chromium/base/sync_socket.h b/chromium/base/sync_socket.h
index 8ba3f6c2652..71addd1e163 100644
--- a/chromium/base/sync_socket.h
+++ b/chromium/base/sync_socket.h
@@ -18,6 +18,7 @@
#include "base/base_export.h"
#include "base/compiler_specific.h"
#include "base/synchronization/waitable_event.h"
+#include "base/time/time.h"
namespace base {
@@ -58,6 +59,13 @@ class BASE_EXPORT SyncSocket {
// Returns the number of bytes received, or 0 upon failure.
virtual size_t Receive(void* buffer, size_t length);
+ // Same as Receive() but only blocks for data until |timeout| has elapsed or
+ // |buffer| |length| is exhausted. Currently only timeouts less than one
+ // second are allowed. Return the amount of data read.
+ virtual size_t ReceiveWithTimeout(void* buffer,
+ size_t length,
+ TimeDelta timeout);
+
// Returns the number of bytes available. If non-zero, Receive() will not
// not block when called. NOTE: Some implementations cannot reliably
// determine the number of bytes available so avoid using the returned
@@ -102,6 +110,9 @@ class BASE_EXPORT CancelableSyncSocket : public SyncSocket {
// SyncSocket methods in order to support shutting down the 'socket'.
virtual bool Close() OVERRIDE;
virtual size_t Receive(void* buffer, size_t length) OVERRIDE;
+ virtual size_t ReceiveWithTimeout(void* buffer,
+ size_t length,
+ TimeDelta timeout) OVERRIDE;
#endif
// Send() is overridden to catch cases where the remote end is not responding
diff --git a/chromium/base/sync_socket_nacl.cc b/chromium/base/sync_socket_nacl.cc
index 7bab8840c01..f6d17e7ffb6 100644
--- a/chromium/base/sync_socket_nacl.cc
+++ b/chromium/base/sync_socket_nacl.cc
@@ -11,7 +11,6 @@
#include "base/logging.h"
-
namespace base {
const SyncSocket::Handle SyncSocket::kInvalidHandle = -1;
@@ -20,6 +19,7 @@ SyncSocket::SyncSocket() : handle_(kInvalidHandle) {
}
SyncSocket::~SyncSocket() {
+ Close();
}
// static
@@ -31,22 +31,29 @@ bool SyncSocket::Close() {
if (handle_ != kInvalidHandle) {
if (close(handle_) < 0)
DPLOG(ERROR) << "close";
- handle_ = -1;
+ handle_ = kInvalidHandle;
}
return true;
}
size_t SyncSocket::Send(const void* buffer, size_t length) {
- // Not implemented since it's not needed by any client code yet.
- return -1;
+ const ssize_t bytes_written = write(handle_, buffer, length);
+ return bytes_written > 0 ? bytes_written : 0;
}
size_t SyncSocket::Receive(void* buffer, size_t length) {
- return read(handle_, buffer, length);
+ const ssize_t bytes_read = read(handle_, buffer, length);
+ return bytes_read > 0 ? bytes_read : 0;
+}
+
+size_t SyncSocket::ReceiveWithTimeout(void* buffer, size_t length, TimeDelta) {
+ NOTIMPLEMENTED();
+ return 0;
}
size_t SyncSocket::Peek() {
- return -1;
+ NOTIMPLEMENTED();
+ return 0;
}
CancelableSyncSocket::CancelableSyncSocket() {
@@ -57,11 +64,11 @@ CancelableSyncSocket::CancelableSyncSocket(Handle handle)
}
size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
- return -1;
+ return SyncSocket::Send(buffer, length);
}
bool CancelableSyncSocket::Shutdown() {
- return false;
+ return SyncSocket::Close();
}
// static
diff --git a/chromium/base/sync_socket_posix.cc b/chromium/base/sync_socket_posix.cc
index 257916df335..5bb893aae87 100644
--- a/chromium/base/sync_socket_posix.cc
+++ b/chromium/base/sync_socket_posix.cc
@@ -5,8 +5,8 @@
#include "base/sync_socket.h"
#include <errno.h>
-#include <limits.h>
#include <fcntl.h>
+#include <limits.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
@@ -18,7 +18,7 @@
#include "base/file_util.h"
#include "base/logging.h"
-
+#include "base/threading/thread_restrictions.h"
namespace base {
@@ -27,6 +27,28 @@ namespace {
// we clamp message lengths, which are size_t, to no more than INT_MAX.
const size_t kMaxMessageLength = static_cast<size_t>(INT_MAX);
+// Writes |length| of |buffer| into |handle|. Returns the number of bytes
+// written or zero on error. |length| must be greater than 0.
+size_t SendHelper(SyncSocket::Handle handle,
+ const void* buffer,
+ size_t length) {
+ DCHECK_GT(length, 0u);
+ 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);
+ return len < 0 ? 0 : static_cast<size_t>(len);
+}
+
+bool CloseHandle(SyncSocket::Handle handle) {
+ if (handle != SyncSocket::kInvalidHandle && close(handle) < 0) {
+ DPLOG(ERROR) << "close";
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
const SyncSocket::Handle SyncSocket::kInvalidHandle = -1;
@@ -39,17 +61,20 @@ SyncSocket::~SyncSocket() {
// static
bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
- DCHECK(socket_a != socket_b);
- DCHECK(socket_a->handle_ == kInvalidHandle);
- DCHECK(socket_b->handle_ == kInvalidHandle);
+ DCHECK_NE(socket_a, socket_b);
+ DCHECK_EQ(socket_a->handle_, kInvalidHandle);
+ DCHECK_EQ(socket_b->handle_, kInvalidHandle);
#if defined(OS_MACOSX)
int nosigpipe = 1;
#endif // defined(OS_MACOSX)
Handle handles[2] = { kInvalidHandle, kInvalidHandle };
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, handles) != 0)
- goto cleanup;
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, handles) != 0) {
+ CloseHandle(handles[0]);
+ CloseHandle(handles[1]);
+ return false;
+ }
#if defined(OS_MACOSX)
// On OSX an attempt to read or write to a closed socket may generate a
@@ -58,7 +83,9 @@ bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
&nosigpipe, sizeof nosigpipe) ||
0 != setsockopt(handles[1], SOL_SOCKET, SO_NOSIGPIPE,
&nosigpipe, sizeof nosigpipe)) {
- goto cleanup;
+ CloseHandle(handles[0]);
+ CloseHandle(handles[1]);
+ return false;
}
#endif
@@ -67,54 +94,101 @@ bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
socket_b->handle_ = handles[1];
return true;
-
- cleanup:
- if (handles[0] != kInvalidHandle) {
- if (HANDLE_EINTR(close(handles[0])) < 0)
- DPLOG(ERROR) << "close";
- }
- if (handles[1] != kInvalidHandle) {
- if (HANDLE_EINTR(close(handles[1])) < 0)
- DPLOG(ERROR) << "close";
- }
-
- return false;
}
bool SyncSocket::Close() {
- if (handle_ == kInvalidHandle) {
- return false;
- }
- int retval = HANDLE_EINTR(close(handle_));
- if (retval < 0)
- DPLOG(ERROR) << "close";
+ const bool retval = CloseHandle(handle_);
handle_ = kInvalidHandle;
- return (retval == 0);
+ return retval;
}
size_t SyncSocket::Send(const void* buffer, size_t length) {
- DCHECK_LE(length, kMaxMessageLength);
- const char* charbuffer = static_cast<const char*>(buffer);
- int len = file_util::WriteFileDescriptor(handle_, charbuffer, length);
-
- return (len == -1) ? 0 : static_cast<size_t>(len);
+ ThreadRestrictions::AssertIOAllowed();
+ return SendHelper(handle_, buffer, length);
}
size_t SyncSocket::Receive(void* buffer, size_t length) {
+ ThreadRestrictions::AssertIOAllowed();
+ DCHECK_GT(length, 0u);
DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(handle_, kInvalidHandle);
char* charbuffer = static_cast<char*>(buffer);
- if (file_util::ReadFromFD(handle_, charbuffer, length))
+ if (ReadFromFD(handle_, charbuffer, length))
return length;
return 0;
}
+size_t SyncSocket::ReceiveWithTimeout(void* buffer,
+ size_t length,
+ TimeDelta timeout) {
+ ThreadRestrictions::AssertIOAllowed();
+ DCHECK_GT(length, 0u);
+ DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(handle_, kInvalidHandle);
+
+ // TODO(dalecurtis): There's an undiagnosed issue on OSX where we're seeing
+ // large numbers of open files which prevents select() from being used. In
+ // this case, the best we can do is Peek() to see if we can Receive() now or
+ // return a timeout error (0) if not. See http://crbug.com/314364.
+ if (handle_ >= FD_SETSIZE)
+ return Peek() < length ? 0 : Receive(buffer, length);
+
+ // Only timeouts greater than zero and less than one second are allowed.
+ DCHECK_GT(timeout.InMicroseconds(), 0);
+ DCHECK_LT(timeout.InMicroseconds(),
+ base::TimeDelta::FromSeconds(1).InMicroseconds());
+
+ // Track the start time so we can reduce the timeout as data is read.
+ TimeTicks start_time = TimeTicks::Now();
+ const TimeTicks finish_time = start_time + timeout;
+
+ fd_set read_fds;
+ size_t bytes_read_total;
+ for (bytes_read_total = 0;
+ bytes_read_total < length && timeout.InMicroseconds() > 0;
+ timeout = finish_time - base::TimeTicks::Now()) {
+ FD_ZERO(&read_fds);
+ FD_SET(handle_, &read_fds);
+
+ // Wait for data to become available.
+ struct timeval timeout_struct =
+ { 0, static_cast<suseconds_t>(timeout.InMicroseconds()) };
+ const int select_result =
+ select(handle_ + 1, &read_fds, NULL, NULL, &timeout_struct);
+ // Handle EINTR manually since we need to update the timeout value.
+ if (select_result == -1 && errno == EINTR)
+ continue;
+ if (select_result <= 0)
+ return bytes_read_total;
+
+ // select() only tells us that data is ready for reading, not how much. We
+ // must Peek() for the amount ready for reading to avoid blocking.
+ DCHECK(FD_ISSET(handle_, &read_fds));
+ const size_t bytes_to_read = std::min(Peek(), length - bytes_read_total);
+
+ // There may be zero bytes to read if the socket at the other end closed.
+ if (!bytes_to_read)
+ return bytes_read_total;
+
+ const size_t bytes_received =
+ Receive(static_cast<char*>(buffer) + bytes_read_total, bytes_to_read);
+ bytes_read_total += bytes_received;
+ if (bytes_received != bytes_to_read)
+ return bytes_read_total;
+ }
+
+ return bytes_read_total;
+}
+
size_t SyncSocket::Peek() {
- int number_chars;
- if (-1 == ioctl(handle_, FIONREAD, &number_chars)) {
+ DCHECK_NE(handle_, kInvalidHandle);
+ int number_chars = 0;
+ if (ioctl(handle_, FIONREAD, &number_chars) == -1) {
// If there is an error in ioctl, signal that the channel would block.
return 0;
}
- return (size_t) number_chars;
+ DCHECK_GE(number_chars, 0);
+ return number_chars;
}
CancelableSyncSocket::CancelableSyncSocket() {}
@@ -123,19 +197,23 @@ CancelableSyncSocket::CancelableSyncSocket(Handle handle)
}
bool CancelableSyncSocket::Shutdown() {
- return HANDLE_EINTR(shutdown(handle(), SHUT_RDWR)) >= 0;
+ DCHECK_NE(handle_, kInvalidHandle);
+ return HANDLE_EINTR(shutdown(handle_, SHUT_RDWR)) >= 0;
}
size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
- long flags = 0;
- flags = fcntl(handle_, F_GETFL, NULL);
+ DCHECK_GT(length, 0u);
+ DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(handle_, kInvalidHandle);
+
+ const long flags = fcntl(handle_, F_GETFL, NULL);
if (flags != -1 && (flags & O_NONBLOCK) == 0) {
// Set the socket to non-blocking mode for sending if its original mode
// is blocking.
fcntl(handle_, F_SETFL, flags | O_NONBLOCK);
}
- size_t len = SyncSocket::Send(buffer, length);
+ const size_t len = SendHelper(handle_, buffer, length);
if (flags != -1 && (flags & O_NONBLOCK) == 0) {
// Restore the original flags.
diff --git a/chromium/base/sync_socket_unittest.cc b/chromium/base/sync_socket_unittest.cc
new file mode 100644
index 00000000000..7e4089cb53c
--- /dev/null
+++ b/chromium/base/sync_socket_unittest.cc
@@ -0,0 +1,131 @@
+// 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/basictypes.h"
+#include "base/sync_socket.h"
+#include "base/threading/simple_thread.h"
+#include "base/time/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int kReceiveTimeoutInMilliseconds = 750;
+
+class HangingReceiveThread : public base::DelegateSimpleThread::Delegate {
+ public:
+ explicit HangingReceiveThread(base::SyncSocket* socket)
+ : socket_(socket),
+ thread_(this, "HangingReceiveThread") {
+ thread_.Start();
+ }
+
+ virtual ~HangingReceiveThread() {}
+
+ virtual void Run() OVERRIDE {
+ int data = 0;
+ ASSERT_EQ(socket_->Peek(), 0u);
+
+ // Use receive with timeout so we don't hang the test harness indefinitely.
+ ASSERT_EQ(0u, socket_->ReceiveWithTimeout(
+ &data, sizeof(data), base::TimeDelta::FromMilliseconds(
+ kReceiveTimeoutInMilliseconds)));
+ }
+
+ void Stop() {
+ thread_.Join();
+ }
+
+ private:
+ base::SyncSocket* socket_;
+ base::DelegateSimpleThread thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(HangingReceiveThread);
+};
+
+// Tests sending data between two SyncSockets. Uses ASSERT() and thus will exit
+// early upon failure. Callers should use ASSERT_NO_FATAL_FAILURE() if testing
+// continues after return.
+void SendReceivePeek(base::SyncSocket* socket_a, base::SyncSocket* socket_b) {
+ int received = 0;
+ const int kSending = 123;
+ COMPILE_ASSERT(sizeof(kSending) == sizeof(received), Invalid_Data_Size);
+
+ ASSERT_EQ(0u, socket_a->Peek());
+ ASSERT_EQ(0u, socket_b->Peek());
+
+ // Verify |socket_a| can send to |socket_a| and |socket_a| can Receive from
+ // |socket_a|.
+ ASSERT_EQ(sizeof(kSending), socket_a->Send(&kSending, sizeof(kSending)));
+ ASSERT_EQ(sizeof(kSending), socket_b->Peek());
+ ASSERT_EQ(sizeof(kSending), socket_b->Receive(&received, sizeof(kSending)));
+ ASSERT_EQ(kSending, received);
+
+ ASSERT_EQ(0u, socket_a->Peek());
+ ASSERT_EQ(0u, socket_b->Peek());
+
+ // Now verify the reverse.
+ received = 0;
+ ASSERT_EQ(sizeof(kSending), socket_b->Send(&kSending, sizeof(kSending)));
+ ASSERT_EQ(sizeof(kSending), socket_a->Peek());
+ ASSERT_EQ(sizeof(kSending), socket_a->Receive(&received, sizeof(kSending)));
+ ASSERT_EQ(kSending, received);
+
+ ASSERT_EQ(0u, socket_a->Peek());
+ ASSERT_EQ(0u, socket_b->Peek());
+
+ ASSERT_TRUE(socket_a->Close());
+ ASSERT_TRUE(socket_b->Close());
+}
+
+template <class SocketType>
+void NormalSendReceivePeek() {
+ SocketType socket_a, socket_b;
+ ASSERT_TRUE(SocketType::CreatePair(&socket_a, &socket_b));
+ SendReceivePeek(&socket_a, &socket_b);
+}
+
+template <class SocketType>
+void ClonedSendReceivePeek() {
+ SocketType socket_a, socket_b;
+ ASSERT_TRUE(SocketType::CreatePair(&socket_a, &socket_b));
+
+ // Create new SyncSockets from the paired handles.
+ SocketType socket_c(socket_a.handle()), socket_d(socket_b.handle());
+ SendReceivePeek(&socket_c, &socket_d);
+}
+
+} // namespace
+
+TEST(SyncSocket, NormalSendReceivePeek) {
+ NormalSendReceivePeek<base::SyncSocket>();
+}
+
+TEST(SyncSocket, ClonedSendReceivePeek) {
+ ClonedSendReceivePeek<base::SyncSocket>();
+}
+
+TEST(CancelableSyncSocket, NormalSendReceivePeek) {
+ NormalSendReceivePeek<base::CancelableSyncSocket>();
+}
+
+TEST(CancelableSyncSocket, ClonedSendReceivePeek) {
+ ClonedSendReceivePeek<base::CancelableSyncSocket>();
+}
+
+TEST(CancelableSyncSocket, CancelReceiveShutdown) {
+ base::CancelableSyncSocket socket_a, socket_b;
+ ASSERT_TRUE(base::CancelableSyncSocket::CreatePair(&socket_a, &socket_b));
+
+ base::TimeTicks start = base::TimeTicks::Now();
+ HangingReceiveThread thread(&socket_b);
+ ASSERT_TRUE(socket_b.Shutdown());
+ thread.Stop();
+
+ // Ensure the receive didn't just timeout.
+ ASSERT_LT((base::TimeTicks::Now() - start).InMilliseconds(),
+ kReceiveTimeoutInMilliseconds);
+
+ ASSERT_TRUE(socket_a.Close());
+ ASSERT_TRUE(socket_b.Close());
+}
diff --git a/chromium/base/sync_socket_win.cc b/chromium/base/sync_socket_win.cc
index 99a6afea3ec..26e76ec2738 100644
--- a/chromium/base/sync_socket_win.cc
+++ b/chromium/base/sync_socket_win.cc
@@ -5,6 +5,7 @@
#include "base/sync_socket.h"
#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
#include "base/win/scoped_handle.h"
namespace base {
@@ -27,9 +28,9 @@ const int kInBufferSize = 4096;
const int kDefaultTimeoutMilliSeconds = 1000;
bool CreatePairImpl(HANDLE* socket_a, HANDLE* socket_b, bool overlapped) {
- DCHECK(socket_a != socket_b);
- DCHECK(*socket_a == SyncSocket::kInvalidHandle);
- DCHECK(*socket_b == SyncSocket::kInvalidHandle);
+ DCHECK_NE(socket_a, socket_b);
+ DCHECK_EQ(*socket_a, SyncSocket::kInvalidHandle);
+ DCHECK_EQ(*socket_b, SyncSocket::kInvalidHandle);
wchar_t name[kPipePathMax];
ScopedHandle handle_a;
@@ -110,35 +111,53 @@ DWORD GetNextChunkSize(size_t current_pos, size_t max_size) {
// on an event that can be used to cancel the operation. If the operation
// is cancelled, the function returns and closes the relevant socket object.
template <typename BufferType, typename Function>
-size_t CancelableFileOperation(Function operation, HANDLE file,
- BufferType* buffer, size_t length,
- base::WaitableEvent* io_event,
- base::WaitableEvent* cancel_event,
+size_t CancelableFileOperation(Function operation,
+ HANDLE file,
+ BufferType* buffer,
+ size_t length,
+ WaitableEvent* io_event,
+ WaitableEvent* cancel_event,
CancelableSyncSocket* socket,
DWORD timeout_in_ms) {
+ ThreadRestrictions::AssertIOAllowed();
// The buffer must be byte size or the length check won't make much sense.
COMPILE_ASSERT(sizeof(buffer[0]) == sizeof(char), incorrect_buffer_type);
+ DCHECK_GT(length, 0u);
DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(file, SyncSocket::kInvalidHandle);
+
+ // Track the finish time so we can calculate the timeout as data is read.
+ TimeTicks current_time, finish_time;
+ if (timeout_in_ms != INFINITE) {
+ current_time = TimeTicks::Now();
+ finish_time =
+ current_time + base::TimeDelta::FromMilliseconds(timeout_in_ms);
+ }
- OVERLAPPED ol = {0};
- ol.hEvent = io_event->handle();
size_t count = 0;
- while (count < length) {
- DWORD chunk = GetNextChunkSize(count, length);
+ do {
+ // The OVERLAPPED structure will be modified by ReadFile or WriteFile.
+ OVERLAPPED ol = { 0 };
+ ol.hEvent = io_event->handle();
+
+ const DWORD chunk = GetNextChunkSize(count, length);
// This is either the ReadFile or WriteFile call depending on whether
// we're receiving or sending data.
DWORD len = 0;
- BOOL ok = operation(file, static_cast<BufferType*>(buffer) + count, chunk,
- &len, &ol);
- if (!ok) {
+ const BOOL operation_ok = operation(
+ file, static_cast<BufferType*>(buffer) + count, chunk, &len, &ol);
+ if (!operation_ok) {
if (::GetLastError() == ERROR_IO_PENDING) {
HANDLE events[] = { io_event->handle(), cancel_event->handle() };
- int wait_result = WaitForMultipleObjects(
- arraysize(events), events, FALSE, timeout_in_ms);
+ const int wait_result = WaitForMultipleObjects(
+ ARRAYSIZE_UNSAFE(events), events, FALSE,
+ timeout_in_ms == INFINITE
+ ? timeout_in_ms
+ : (finish_time - current_time).InMilliseconds());
if (wait_result == (WAIT_OBJECT_0 + 0)) {
GetOverlappedResult(file, &ol, &len, TRUE);
} else if (wait_result == (WAIT_OBJECT_0 + 1)) {
- VLOG(1) << "Shutdown was signaled. Closing socket.";
+ DVLOG(1) << "Shutdown was signaled. Closing socket.";
CancelIo(file);
socket->Close();
count = 0;
@@ -146,9 +165,8 @@ size_t CancelableFileOperation(Function operation, HANDLE file,
} else {
// Timeout happened.
DCHECK_EQ(WAIT_TIMEOUT, wait_result);
- if (!CancelIo(file)){
+ if (!CancelIo(file))
DLOG(WARNING) << "CancelIo() failed";
- }
break;
}
} else {
@@ -161,9 +179,15 @@ size_t CancelableFileOperation(Function operation, HANDLE file,
// Quit the operation if we can't write/read anymore.
if (len != chunk)
break;
- }
- return (count > 0) ? count : 0;
+ // Since TimeTicks::Now() is expensive, only bother updating the time if we
+ // have more work to do.
+ if (timeout_in_ms != INFINITE && count < length)
+ current_time = base::TimeTicks::Now();
+ } while (count < length &&
+ (timeout_in_ms == INFINITE || current_time < finish_time));
+
+ return count;
}
} // namespace
@@ -185,37 +209,50 @@ bool SyncSocket::CreatePair(SyncSocket* socket_a, SyncSocket* socket_b) {
bool SyncSocket::Close() {
if (handle_ == kInvalidHandle)
- return false;
+ return true;
- BOOL retval = CloseHandle(handle_);
+ const BOOL result = CloseHandle(handle_);
handle_ = kInvalidHandle;
- return retval ? true : false;
+ return result == TRUE;
}
size_t SyncSocket::Send(const void* buffer, size_t length) {
+ ThreadRestrictions::AssertIOAllowed();
+ DCHECK_GT(length, 0u);
DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(handle_, kInvalidHandle);
size_t count = 0;
while (count < length) {
DWORD len;
DWORD chunk = GetNextChunkSize(count, length);
if (WriteFile(handle_, static_cast<const char*>(buffer) + count,
chunk, &len, NULL) == FALSE) {
- return (0 < count) ? count : 0;
+ return count;
}
count += len;
}
return count;
}
+size_t SyncSocket::ReceiveWithTimeout(void* buffer,
+ size_t length,
+ TimeDelta timeout) {
+ NOTIMPLEMENTED();
+ return 0;
+}
+
size_t SyncSocket::Receive(void* buffer, size_t length) {
+ ThreadRestrictions::AssertIOAllowed();
+ DCHECK_GT(length, 0u);
DCHECK_LE(length, kMaxMessageLength);
+ DCHECK_NE(handle_, kInvalidHandle);
size_t count = 0;
while (count < length) {
DWORD len;
DWORD chunk = GetNextChunkSize(count, length);
if (ReadFile(handle_, static_cast<char*>(buffer) + count,
chunk, &len, NULL) == FALSE) {
- return (0 < count) ? count : 0;
+ return count;
}
count += len;
}
@@ -245,9 +282,9 @@ bool CancelableSyncSocket::Shutdown() {
}
bool CancelableSyncSocket::Close() {
- bool ret = SyncSocket::Close();
+ const bool result = SyncSocket::Close();
shutdown_event_.Reset();
- return ret;
+ return result;
}
size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
@@ -258,9 +295,17 @@ size_t CancelableSyncSocket::Send(const void* buffer, size_t length) {
}
size_t CancelableSyncSocket::Receive(void* buffer, size_t length) {
- return CancelableFileOperation(&ReadFile, handle_,
- reinterpret_cast<char*>(buffer), length, &file_operation_,
- &shutdown_event_, this, INFINITE);
+ return CancelableFileOperation(
+ &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
+ &file_operation_, &shutdown_event_, this, INFINITE);
+}
+
+size_t CancelableSyncSocket::ReceiveWithTimeout(void* buffer,
+ size_t length,
+ TimeDelta timeout) {
+ return CancelableFileOperation(
+ &ReadFile, handle_, reinterpret_cast<char*>(buffer), length,
+ &file_operation_, &shutdown_event_, this, timeout.InMilliseconds());
}
// static
@@ -269,5 +314,4 @@ bool CancelableSyncSocket::CreatePair(CancelableSyncSocket* socket_a,
return CreatePairImpl(&socket_a->handle_, &socket_b->handle_, true);
}
-
} // namespace base
diff --git a/chromium/base/synchronization/condition_variable_posix.cc b/chromium/base/synchronization/condition_variable_posix.cc
index 92b479ede47..e70a301cb2a 100644
--- a/chromium/base/synchronization/condition_variable_posix.cc
+++ b/chromium/base/synchronization/condition_variable_posix.cc
@@ -20,7 +20,22 @@ ConditionVariable::ConditionVariable(Lock* user_lock)
, user_lock_(user_lock)
#endif
{
- int rv = pthread_cond_init(&condition_, NULL);
+ 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.
+ // Mac can use relative time deadlines.
+#if !defined(OS_MACOSX) && !defined(OS_NACL) && !defined(OS_ANDROID)
+ pthread_condattr_t attrs;
+ rv = pthread_condattr_init(&attrs);
+ DCHECK_EQ(0, rv);
+ pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ rv = pthread_cond_init(&condition_, &attrs);
+ pthread_condattr_destroy(&attrs);
+#else
+ rv = pthread_cond_init(&condition_, NULL);
+#endif
DCHECK_EQ(0, rv);
}
@@ -44,23 +59,48 @@ void ConditionVariable::Wait() {
void ConditionVariable::TimedWait(const TimeDelta& max_time) {
base::ThreadRestrictions::AssertWaitAllowed();
int64 usecs = max_time.InMicroseconds();
+ struct timespec relative_time;
+ relative_time.tv_sec = usecs / Time::kMicrosecondsPerSecond;
+ relative_time.tv_nsec =
+ (usecs % Time::kMicrosecondsPerSecond) * Time::kNanosecondsPerMicrosecond;
+
+#if !defined(NDEBUG)
+ user_lock_->CheckHeldAndUnmark();
+#endif
+#if defined(OS_MACOSX)
+ int rv = pthread_cond_timedwait_relative_np(
+ &condition_, user_mutex_, &relative_time);
+#else
// The timeout argument to pthread_cond_timedwait is in absolute time.
+ struct timespec absolute_time;
+#if defined(OS_NACL)
+ // See comment in constructor for why this is different in NaCl.
struct timeval now;
gettimeofday(&now, NULL);
+ absolute_time.tv_sec = now.tv_sec;
+ absolute_time.tv_nsec = now.tv_usec * Time::kNanosecondsPerMicrosecond;
+#else
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ absolute_time.tv_sec = now.tv_sec;
+ absolute_time.tv_nsec = now.tv_nsec;
+#endif
- struct timespec abstime;
- abstime.tv_sec = now.tv_sec + (usecs / Time::kMicrosecondsPerSecond);
- abstime.tv_nsec = (now.tv_usec + (usecs % Time::kMicrosecondsPerSecond)) *
- Time::kNanosecondsPerMicrosecond;
- abstime.tv_sec += abstime.tv_nsec / Time::kNanosecondsPerSecond;
- abstime.tv_nsec %= Time::kNanosecondsPerSecond;
- DCHECK_GE(abstime.tv_sec, now.tv_sec); // Overflow paranoia
+ absolute_time.tv_sec += relative_time.tv_sec;
+ absolute_time.tv_nsec += relative_time.tv_nsec;
+ absolute_time.tv_sec += absolute_time.tv_nsec / Time::kNanosecondsPerSecond;
+ absolute_time.tv_nsec %= Time::kNanosecondsPerSecond;
+ DCHECK_GE(absolute_time.tv_sec, now.tv_sec); // Overflow paranoia
+
+#if defined(OS_ANDROID)
+ 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_MACOSX
-#if !defined(NDEBUG)
- user_lock_->CheckHeldAndUnmark();
-#endif
- int rv = pthread_cond_timedwait(&condition_, user_mutex_, &abstime);
DCHECK(rv == 0 || rv == ETIMEDOUT);
#if !defined(NDEBUG)
user_lock_->CheckUnheldAndMark();
diff --git a/chromium/base/synchronization/condition_variable_unittest.cc b/chromium/base/synchronization/condition_variable_unittest.cc
index 4230e635495..ee554ff329f 100644
--- a/chromium/base/synchronization/condition_variable_unittest.cc
+++ b/chromium/base/synchronization/condition_variable_unittest.cc
@@ -8,12 +8,14 @@
#include <algorithm>
#include <vector>
+#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/synchronization/spin_wait.h"
#include "base/threading/platform_thread.h"
+#include "base/threading/thread.h"
#include "base/threading/thread_collision_warner.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -188,9 +190,60 @@ TEST_F(ConditionVariableTest, TimeoutTest) {
lock.Release();
}
+#if defined(OS_POSIX)
+const int kDiscontinuitySeconds = 2;
+
+void BackInTime(Lock* lock) {
+ AutoLock auto_lock(*lock);
+
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ tv.tv_sec -= kDiscontinuitySeconds;
+ settimeofday(&tv, NULL);
+}
+
+// Tests that TimedWait ignores changes to the system clock.
+// Test is disabled by default, because it needs to run as root to muck with the
+// system clock.
+// http://crbug.com/293736
+TEST_F(ConditionVariableTest, DISABLED_TimeoutAcrossSetTimeOfDay) {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ tv.tv_sec += kDiscontinuitySeconds;
+ if (settimeofday(&tv, NULL) < 0) {
+ PLOG(ERROR) << "Could not set time of day. Run as root?";
+ return;
+ }
+
+ Lock lock;
+ ConditionVariable cv(&lock);
+ lock.Acquire();
+
+ Thread thread("Helper");
+ thread.Start();
+ thread.message_loop()->PostTask(FROM_HERE, base::Bind(&BackInTime, &lock));
+
+ TimeTicks start = TimeTicks::Now();
+ const TimeDelta kWaitTime = TimeDelta::FromMilliseconds(300);
+ // Allow for clocking rate granularity.
+ const TimeDelta kFudgeTime = TimeDelta::FromMilliseconds(50);
+
+ cv.TimedWait(kWaitTime + kFudgeTime);
+ TimeDelta duration = TimeTicks::Now() - start;
+
+ thread.Stop();
+ // We can't use EXPECT_GE here as the TimeDelta class does not support the
+ // required stream conversion.
+ EXPECT_TRUE(duration >= kWaitTime);
+ EXPECT_TRUE(duration <= TimeDelta::FromSeconds(kDiscontinuitySeconds));
+
+ lock.Release();
+}
+#endif
+
// Suddenly got flaky on Win, see http://crbug.com/10607 (starting at
-// comment #15)
+// comment #15).
#if defined(OS_WIN)
#define MAYBE_MultiThreadConsumerTest DISABLED_MultiThreadConsumerTest
#else
diff --git a/chromium/base/synchronization/waitable_event_posix.cc b/chromium/base/synchronization/waitable_event_posix.cc
index 714c111e197..fccba9d31c6 100644
--- a/chromium/base/synchronization/waitable_event_posix.cc
+++ b/chromium/base/synchronization/waitable_event_posix.cc
@@ -159,7 +159,7 @@ void WaitableEvent::Wait() {
bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
base::ThreadRestrictions::AssertWaitAllowed();
- const Time end_time(Time::Now() + max_time);
+ const TimeTicks end_time(TimeTicks::Now() + max_time);
const bool finite_time = max_time.ToInternalValue() >= 0;
kernel_->lock_.Acquire();
@@ -184,7 +184,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
// again before unlocking it.
for (;;) {
- const Time current_time(Time::Now());
+ const TimeTicks current_time(TimeTicks::Now());
if (sw.fired() || (finite_time && current_time >= end_time)) {
const bool return_value = sw.fired();
diff --git a/chromium/base/sys_info.h b/chromium/base/sys_info.h
index 38462ed1110..aa40cadfbc9 100644
--- a/chromium/base/sys_info.h
+++ b/chromium/base/sys_info.h
@@ -5,11 +5,13 @@
#ifndef BASE_SYS_INFO_H_
#define BASE_SYS_INFO_H_
+#include <map>
#include <string>
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/files/file_path.h"
+#include "base/time/time.h"
#include "build/build_config.h"
namespace base {
@@ -73,24 +75,34 @@ class BASE_EXPORT SysInfo {
static size_t VMAllocationGranularity();
#if defined(OS_POSIX) && !defined(OS_MACOSX)
- // Returns the maximum SysV shared memory segment size.
+ // Returns the maximum SysV shared memory segment size, or zero if there is no
+ // limit.
static size_t MaxSharedMemorySize();
#endif // defined(OS_POSIX) && !defined(OS_MACOSX)
#if defined(OS_CHROMEOS)
- // Returns the name of the version entry we wish to look up in the
- // Linux Standard Base release information file.
- static std::string GetLinuxStandardBaseVersionKey();
-
- // Parses /etc/lsb-release to get version information for Google Chrome OS.
- // Declared here so it can be exposed for unit testing.
- static void ParseLsbRelease(const std::string& lsb_release,
- int32* major_version,
- int32* minor_version,
- int32* bugfix_version);
-
- // Returns the path to the lsb-release file.
- static FilePath GetLsbReleaseFilePath();
+ typedef std::map<std::string, std::string> LsbReleaseMap;
+
+ // Returns the contents of /etc/lsb-release as a map.
+ static const LsbReleaseMap& GetLsbReleaseMap();
+
+ // If |key| is present in the LsbReleaseMap, sets |value| and returns true.
+ static bool GetLsbReleaseValue(const std::string& key, std::string* value);
+
+ // Convenience function for GetLsbReleaseValue("CHROMEOS_RELEASE_BOARD",...).
+ // Returns "unknown" if CHROMEOS_RELEASE_BOARD is not set.
+ static std::string GetLsbReleaseBoard();
+
+ // Returns the creation time of /etc/lsb-release. (Used to get the date and
+ // time of the Chrome OS build).
+ static Time GetLsbReleaseTime();
+
+ // Returns true when actually running in a Chrome OS environment.
+ static bool IsRunningOnChromeOS();
+
+ // Test method to force re-parsing of lsb-release.
+ static void SetChromeOSVersionInfoForTest(const std::string& lsb_release,
+ const Time& lsb_release_time);
#endif // defined(OS_CHROMEOS)
#if defined(OS_ANDROID)
diff --git a/chromium/base/sys_info_chromeos.cc b/chromium/base/sys_info_chromeos.cc
index 4c1a7af56cb..7cf69753460 100644
--- a/chromium/base/sys_info_chromeos.cc
+++ b/chromium/base/sys_info_chromeos.cc
@@ -5,115 +5,214 @@
#include "base/sys_info.h"
#include "base/basictypes.h"
+#include "base/environment.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
+#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
#include "base/threading/thread_restrictions.h"
namespace base {
-static const char* kLinuxStandardBaseVersionKeys[] = {
+namespace {
+
+const char* kLinuxStandardBaseVersionKeys[] = {
"CHROMEOS_RELEASE_VERSION",
"GOOGLE_RELEASE",
"DISTRIB_RELEASE",
- NULL
+};
+
+const char kChromeOsReleaseNameKey[] = "CHROMEOS_RELEASE_NAME";
+
+const char* const kChromeOsReleaseNames[] = {
+ "Chrome OS",
+ "Chromium OS",
};
const char kLinuxStandardBaseReleaseFile[] = "/etc/lsb-release";
-struct ChromeOSVersionNumbers {
- ChromeOSVersionNumbers()
- : major_version(0),
- minor_version(0),
- bugfix_version(0),
- parsed(false) {
+const char kLsbReleaseKey[] = "LSB_RELEASE";
+const char kLsbReleaseTimeKey[] = "LSB_RELEASE_TIME"; // Seconds since epoch
+
+const char kLsbReleaseSourceKey[] = "lsb-release";
+const char kLsbReleaseSourceEnv[] = "env";
+const char kLsbReleaseSourceFile[] = "file";
+
+class ChromeOSVersionInfo {
+ public:
+ ChromeOSVersionInfo() {
+ Parse();
+ }
+
+ void Parse() {
+ lsb_release_map_.clear();
+ major_version_ = 0;
+ minor_version_ = 0;
+ bugfix_version_ = 0;
+ is_running_on_chromeos_ = false;
+
+ std::string lsb_release, lsb_release_time_str;
+ scoped_ptr<Environment> env(Environment::Create());
+ bool parsed_from_env =
+ env->GetVar(kLsbReleaseKey, &lsb_release) &&
+ env->GetVar(kLsbReleaseTimeKey, &lsb_release_time_str);
+ if (parsed_from_env) {
+ double us = 0;
+ if (StringToDouble(lsb_release_time_str, &us))
+ lsb_release_time_ = Time::FromDoubleT(us);
+ } else {
+ // If the LSB_RELEASE and LSB_RELEASE_TIME environment variables are not
+ // set, fall back to a blocking read of the lsb_release file. This should
+ // only happen in non Chrome OS environments.
+ ThreadRestrictions::ScopedAllowIO allow_io;
+ FilePath path(kLinuxStandardBaseReleaseFile);
+ ReadFileToString(path, &lsb_release);
+ PlatformFileInfo fileinfo;
+ if (GetFileInfo(path, &fileinfo))
+ lsb_release_time_ = fileinfo.creation_time;
+ }
+ ParseLsbRelease(lsb_release);
+ // For debugging:
+ lsb_release_map_[kLsbReleaseSourceKey] =
+ parsed_from_env ? kLsbReleaseSourceEnv : kLsbReleaseSourceFile;
+ }
+
+ bool GetLsbReleaseValue(const std::string& key, std::string* value) {
+ SysInfo::LsbReleaseMap::const_iterator iter = lsb_release_map_.find(key);
+ if (iter == lsb_release_map_.end())
+ return false;
+ *value = iter->second;
+ return true;
+ }
+
+ void GetVersionNumbers(int32* major_version,
+ int32* minor_version,
+ int32* bugfix_version) {
+ *major_version = major_version_;
+ *minor_version = minor_version_;
+ *bugfix_version = bugfix_version_;
}
- int32 major_version;
- int32 minor_version;
- int32 bugfix_version;
- bool parsed;
+ const Time& lsb_release_time() const { return lsb_release_time_; }
+ const SysInfo::LsbReleaseMap& lsb_release_map() const {
+ return lsb_release_map_;
+ }
+ bool is_running_on_chromeos() const { return is_running_on_chromeos_; }
+
+ private:
+ void ParseLsbRelease(const std::string& lsb_release) {
+ // Parse and cache lsb_release key pairs. There should only be a handful
+ // of entries so the overhead for this will be small, and it can be
+ // useful for debugging.
+ std::vector<std::pair<std::string, std::string> > pairs;
+ SplitStringIntoKeyValuePairs(lsb_release, '=', '\n', &pairs);
+ for (size_t i = 0; i < pairs.size(); ++i) {
+ std::string key, value;
+ TrimWhitespaceASCII(pairs[i].first, TRIM_ALL, &key);
+ TrimWhitespaceASCII(pairs[i].second, TRIM_ALL, &value);
+ if (key.empty())
+ continue;
+ lsb_release_map_[key] = value;
+ }
+ // Parse the version from the first matching recognized version key.
+ std::string version;
+ for (size_t i = 0; i < arraysize(kLinuxStandardBaseVersionKeys); ++i) {
+ std::string key = kLinuxStandardBaseVersionKeys[i];
+ if (GetLsbReleaseValue(key, &version) && !version.empty())
+ break;
+ }
+ StringTokenizer tokenizer(version, ".");
+ if (tokenizer.GetNext()) {
+ StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()),
+ &major_version_);
+ }
+ if (tokenizer.GetNext()) {
+ StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()),
+ &minor_version_);
+ }
+ if (tokenizer.GetNext()) {
+ StringToInt(StringPiece(tokenizer.token_begin(), tokenizer.token_end()),
+ &bugfix_version_);
+ }
+
+ // Check release name for Chrome OS.
+ std::string release_name;
+ if (GetLsbReleaseValue(kChromeOsReleaseNameKey, &release_name)) {
+ for (size_t i = 0; i < arraysize(kChromeOsReleaseNames); ++i) {
+ if (release_name == kChromeOsReleaseNames[i]) {
+ is_running_on_chromeos_ = true;
+ break;
+ }
+ }
+ }
+ }
+
+ Time lsb_release_time_;
+ SysInfo::LsbReleaseMap lsb_release_map_;
+ int32 major_version_;
+ int32 minor_version_;
+ int32 bugfix_version_;
+ bool is_running_on_chromeos_;
};
-static LazyInstance<ChromeOSVersionNumbers>
- g_chrome_os_version_numbers = LAZY_INSTANCE_INITIALIZER;
+static LazyInstance<ChromeOSVersionInfo>
+ g_chrome_os_version_info = LAZY_INSTANCE_INITIALIZER;
+
+ChromeOSVersionInfo& GetChromeOSVersionInfo() {
+ return g_chrome_os_version_info.Get();
+}
+
+} // namespace
// static
void SysInfo::OperatingSystemVersionNumbers(int32* major_version,
int32* minor_version,
int32* bugfix_version) {
- if (!g_chrome_os_version_numbers.Get().parsed) {
- // The other implementations of SysInfo don't block on the disk.
- // See http://code.google.com/p/chromium/issues/detail?id=60394
- // Perhaps the caller ought to cache this?
- // Temporary allowing while we work the bug out.
- ThreadRestrictions::ScopedAllowIO allow_io;
-
- FilePath path(kLinuxStandardBaseReleaseFile);
- std::string contents;
- if (ReadFileToString(path, &contents)) {
- g_chrome_os_version_numbers.Get().parsed = true;
- ParseLsbRelease(contents,
- &(g_chrome_os_version_numbers.Get().major_version),
- &(g_chrome_os_version_numbers.Get().minor_version),
- &(g_chrome_os_version_numbers.Get().bugfix_version));
- }
- }
- *major_version = g_chrome_os_version_numbers.Get().major_version;
- *minor_version = g_chrome_os_version_numbers.Get().minor_version;
- *bugfix_version = g_chrome_os_version_numbers.Get().bugfix_version;
+ return GetChromeOSVersionInfo().GetVersionNumbers(
+ major_version, minor_version, bugfix_version);
}
// static
-std::string SysInfo::GetLinuxStandardBaseVersionKey() {
- return std::string(kLinuxStandardBaseVersionKeys[0]);
+const SysInfo::LsbReleaseMap& SysInfo::GetLsbReleaseMap() {
+ return GetChromeOSVersionInfo().lsb_release_map();
}
// static
-void SysInfo::ParseLsbRelease(const std::string& lsb_release,
- int32* major_version,
- int32* minor_version,
- int32* bugfix_version) {
- size_t version_key_index = std::string::npos;
- for (int i = 0; kLinuxStandardBaseVersionKeys[i] != NULL; ++i) {
- version_key_index = lsb_release.find(kLinuxStandardBaseVersionKeys[i]);
- if (std::string::npos != version_key_index) {
- break;
- }
- }
- if (std::string::npos == version_key_index) {
- return;
- }
+bool SysInfo::GetLsbReleaseValue(const std::string& key, std::string* value) {
+ return GetChromeOSVersionInfo().GetLsbReleaseValue(key, value);
+}
- size_t start_index = lsb_release.find_first_of('=', version_key_index);
- start_index++; // Move past '='.
- size_t length = lsb_release.find_first_of('\n', start_index) - start_index;
- std::string version = lsb_release.substr(start_index, length);
- StringTokenizer tokenizer(version, ".");
- for (int i = 0; i < 3 && tokenizer.GetNext(); ++i) {
- if (0 == i) {
- StringToInt(StringPiece(tokenizer.token_begin(),
- tokenizer.token_end()),
- major_version);
- *minor_version = *bugfix_version = 0;
- } else if (1 == i) {
- StringToInt(StringPiece(tokenizer.token_begin(),
- tokenizer.token_end()),
- minor_version);
- } else { // 2 == i
- StringToInt(StringPiece(tokenizer.token_begin(),
- tokenizer.token_end()),
- bugfix_version);
- }
- }
+// static
+std::string SysInfo::GetLsbReleaseBoard() {
+ const char kMachineInfoBoard[] = "CHROMEOS_RELEASE_BOARD";
+ std::string board;
+ if (!GetLsbReleaseValue(kMachineInfoBoard, &board))
+ board = "unknown";
+ return board;
+}
+
+// static
+Time SysInfo::GetLsbReleaseTime() {
+ return GetChromeOSVersionInfo().lsb_release_time();
+}
+
+// static
+bool SysInfo::IsRunningOnChromeOS() {
+ return GetChromeOSVersionInfo().is_running_on_chromeos();
}
// static
-FilePath SysInfo::GetLsbReleaseFilePath() {
- return FilePath(kLinuxStandardBaseReleaseFile);
+void SysInfo::SetChromeOSVersionInfoForTest(const std::string& lsb_release,
+ const Time& lsb_release_time) {
+ scoped_ptr<Environment> env(Environment::Create());
+ env->SetVar(kLsbReleaseKey, lsb_release);
+ env->SetVar(kLsbReleaseTimeKey,
+ DoubleToString(lsb_release_time.ToDoubleT()));
+ g_chrome_os_version_info.Get().Parse();
}
} // namespace base
diff --git a/chromium/base/sys_info_internal.h b/chromium/base/sys_info_internal.h
new file mode 100644
index 00000000000..e7674d5c09f
--- /dev/null
+++ b/chromium/base/sys_info_internal.h
@@ -0,0 +1,34 @@
+// 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_SYS_INFO_INTERNAL_H_
+#define BASE_SYS_INFO_INTERNAL_H_
+
+#include "base/basictypes.h"
+
+namespace base {
+
+namespace internal {
+
+template<typename T, T (*F)(void)>
+class LazySysInfoValue {
+ public:
+ LazySysInfoValue()
+ : value_(F()) { }
+
+ ~LazySysInfoValue() { }
+
+ T value() { return value_; }
+
+ private:
+ const T value_;
+
+ DISALLOW_COPY_AND_ASSIGN(LazySysInfoValue);
+};
+
+} // namespace internal
+
+} // namespace base
+
+#endif // BASE_SYS_INFO_INTERNAL_H_
diff --git a/chromium/base/sys_info_linux.cc b/chromium/base/sys_info_linux.cc
index acc477134cd..6f1e5eb7d1f 100644
--- a/chromium/base/sys_info_linux.cc
+++ b/chromium/base/sys_info_linux.cc
@@ -7,8 +7,10 @@
#include <limits>
#include "base/file_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
+#include "base/sys_info_internal.h"
namespace {
@@ -22,41 +24,54 @@ int64 AmountOfMemory(int pages_name) {
return static_cast<int64>(pages) * page_size;
}
+int64 AmountOfPhysicalMemory() {
+ return AmountOfMemory(_SC_PHYS_PAGES);
+}
+
+size_t MaxSharedMemorySize() {
+ std::string contents;
+ base::ReadFileToString(base::FilePath("/proc/sys/kernel/shmmax"), &contents);
+ DCHECK(!contents.empty());
+ if (!contents.empty() && contents[contents.length() - 1] == '\n') {
+ contents.erase(contents.length() - 1);
+ }
+
+ int64 limit;
+ if (!base::StringToInt64(contents, &limit)) {
+ limit = 0;
+ }
+ if (limit < 0 ||
+ static_cast<uint64>(limit) > std::numeric_limits<size_t>::max()) {
+ limit = 0;
+ }
+ DCHECK(limit > 0);
+ return static_cast<size_t>(limit);
+}
+
+base::LazyInstance<
+ base::internal::LazySysInfoValue<int64, AmountOfPhysicalMemory> >::Leaky
+ g_lazy_physical_memory = LAZY_INSTANCE_INITIALIZER;
+base::LazyInstance<
+ base::internal::LazySysInfoValue<size_t, MaxSharedMemorySize> >::Leaky
+ g_lazy_max_shared_memory = LAZY_INSTANCE_INITIALIZER;
+
} // namespace
namespace base {
// static
-int64 SysInfo::AmountOfPhysicalMemory() {
- return AmountOfMemory(_SC_PHYS_PAGES);
+int64 SysInfo::AmountOfAvailablePhysicalMemory() {
+ return AmountOfMemory(_SC_AVPHYS_PAGES);
}
// static
-int64 SysInfo::AmountOfAvailablePhysicalMemory() {
- return AmountOfMemory(_SC_AVPHYS_PAGES);
+int64 SysInfo::AmountOfPhysicalMemory() {
+ return g_lazy_physical_memory.Get().value();
}
// static
size_t SysInfo::MaxSharedMemorySize() {
- static int64 limit;
- static bool limit_valid = false;
- if (!limit_valid) {
- std::string contents;
- ReadFileToString(FilePath("/proc/sys/kernel/shmmax"), &contents);
- DCHECK(!contents.empty());
- if (!contents.empty() && contents[contents.length() - 1] == '\n') {
- contents.erase(contents.length() - 1);
- }
- if (base::StringToInt64(contents, &limit)) {
- DCHECK(limit >= 0);
- DCHECK(static_cast<uint64>(limit) <= std::numeric_limits<size_t>::max());
- limit_valid = true;
- } else {
- NOTREACHED();
- return 0;
- }
- }
- return static_cast<size_t>(limit);
+ return g_lazy_max_shared_memory.Get().value();
}
// static
diff --git a/chromium/base/sys_info_posix.cc b/chromium/base/sys_info_posix.cc
index bbb16625590..07d08b72bbc 100644
--- a/chromium/base/sys_info_posix.cc
+++ b/chromium/base/sys_info_posix.cc
@@ -12,8 +12,10 @@
#include "base/basictypes.h"
#include "base/file_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/sys_info_internal.h"
#include "base/threading/thread_restrictions.h"
#if defined(OS_ANDROID)
@@ -23,10 +25,10 @@
#include <sys/statvfs.h>
#endif
-namespace base {
+namespace {
#if !defined(OS_OPENBSD)
-int SysInfo::NumberOfProcessors() {
+int NumberOfProcessors() {
// It seems that sysconf returns the number of "logical" processors on both
// Mac and Linux. So we get the number of "online logical" processors.
long res = sysconf(_SC_NPROCESSORS_ONLN);
@@ -37,6 +39,20 @@ int SysInfo::NumberOfProcessors() {
return static_cast<int>(res);
}
+
+base::LazyInstance<
+ base::internal::LazySysInfoValue<int, NumberOfProcessors> >::Leaky
+ g_lazy_number_of_processors = LAZY_INSTANCE_INITIALIZER;
+#endif
+
+} // namespace
+
+namespace base {
+
+#if !defined(OS_OPENBSD)
+int SysInfo::NumberOfProcessors() {
+ return g_lazy_number_of_processors.Get().value();
+}
#endif
// static
diff --git a/chromium/base/sys_info_unittest.cc b/chromium/base/sys_info_unittest.cc
index 8153f2b3572..93eff777c9b 100644
--- a/chromium/base/sys_info_unittest.cc
+++ b/chromium/base/sys_info_unittest.cc
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/environment.h"
#include "base/file_util.h"
#include "base/sys_info.h"
#include "base/threading/platform_thread.h"
+#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
@@ -32,12 +34,12 @@ TEST_F(SysInfoTest, AmountOfMem) {
TEST_F(SysInfoTest, AmountOfFreeDiskSpace) {
// We aren't actually testing that it's correct, just that it's sane.
FilePath tmp_path;
- ASSERT_TRUE(file_util::GetTempDir(&tmp_path));
+ ASSERT_TRUE(base::GetTempDir(&tmp_path));
EXPECT_GT(base::SysInfo::AmountOfFreeDiskSpace(tmp_path), 0)
<< tmp_path.value();
}
-#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
+#if defined(OS_WIN) || defined(OS_MACOSX)
TEST_F(SysInfoTest, OperatingSystemVersionNumbers) {
int32 os_major_version = -1;
int32 os_minor_version = -1;
@@ -62,17 +64,18 @@ TEST_F(SysInfoTest, Uptime) {
}
#if defined(OS_CHROMEOS)
+
TEST_F(SysInfoTest, GoogleChromeOSVersionNumbers) {
int32 os_major_version = -1;
int32 os_minor_version = -1;
int32 os_bugfix_version = -1;
- std::string lsb_release("FOO=1234123.34.5\n");
- lsb_release.append(base::SysInfo::GetLinuxStandardBaseVersionKey());
- lsb_release.append("=1.2.3.4\n");
- base::SysInfo::ParseLsbRelease(lsb_release,
- &os_major_version,
- &os_minor_version,
- &os_bugfix_version);
+ const char* kLsbRelease =
+ "FOO=1234123.34.5\n"
+ "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
EXPECT_EQ(1, os_major_version);
EXPECT_EQ(2, os_minor_version);
EXPECT_EQ(3, os_bugfix_version);
@@ -82,13 +85,13 @@ TEST_F(SysInfoTest, GoogleChromeOSVersionNumbersFirst) {
int32 os_major_version = -1;
int32 os_minor_version = -1;
int32 os_bugfix_version = -1;
- std::string lsb_release(base::SysInfo::GetLinuxStandardBaseVersionKey());
- lsb_release.append("=1.2.3.4\n");
- lsb_release.append("FOO=1234123.34.5\n");
- base::SysInfo::ParseLsbRelease(lsb_release,
- &os_major_version,
- &os_minor_version,
- &os_bugfix_version);
+ const char* kLsbRelease =
+ "CHROMEOS_RELEASE_VERSION=1.2.3.4\n"
+ "FOO=1234123.34.5\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
EXPECT_EQ(1, os_major_version);
EXPECT_EQ(2, os_minor_version);
EXPECT_EQ(3, os_bugfix_version);
@@ -98,14 +101,46 @@ TEST_F(SysInfoTest, GoogleChromeOSNoVersionNumbers) {
int32 os_major_version = -1;
int32 os_minor_version = -1;
int32 os_bugfix_version = -1;
- std::string lsb_release("FOO=1234123.34.5\n");
- base::SysInfo::ParseLsbRelease(lsb_release,
- &os_major_version,
- &os_minor_version,
- &os_bugfix_version);
- EXPECT_EQ(-1, os_major_version);
- EXPECT_EQ(-1, os_minor_version);
- EXPECT_EQ(-1, os_bugfix_version);
+ const char* kLsbRelease = "FOO=1234123.34.5\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, base::Time());
+ base::SysInfo::OperatingSystemVersionNumbers(&os_major_version,
+ &os_minor_version,
+ &os_bugfix_version);
+ EXPECT_EQ(0, os_major_version);
+ EXPECT_EQ(0, os_minor_version);
+ EXPECT_EQ(0, os_bugfix_version);
+}
+
+TEST_F(SysInfoTest, GoogleChromeOSLsbReleaseTime) {
+ const char* kLsbRelease = "CHROMEOS_RELEASE_VERSION=1.2.3.4";
+ // Use a fake time that can be safely displayed as a string.
+ const base::Time lsb_release_time(base::Time::FromDoubleT(12345.6));
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease, lsb_release_time);
+ base::Time parsed_lsb_release_time = base::SysInfo::GetLsbReleaseTime();
+ EXPECT_DOUBLE_EQ(lsb_release_time.ToDoubleT(),
+ parsed_lsb_release_time.ToDoubleT());
+}
+
+TEST_F(SysInfoTest, IsRunningOnChromeOS) {
+ base::SysInfo::SetChromeOSVersionInfoForTest("", base::Time());
+ EXPECT_FALSE(base::SysInfo::IsRunningOnChromeOS());
+
+ const char* kLsbRelease1 =
+ "CHROMEOS_RELEASE_NAME=Non Chrome OS\n"
+ "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease1, base::Time());
+ EXPECT_FALSE(base::SysInfo::IsRunningOnChromeOS());
+
+ const char* kLsbRelease2 =
+ "CHROMEOS_RELEASE_NAME=Chrome OS\n"
+ "CHROMEOS_RELEASE_VERSION=1.2.3.4\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease2, base::Time());
+ EXPECT_TRUE(base::SysInfo::IsRunningOnChromeOS());
+
+ const char* kLsbRelease3 =
+ "CHROMEOS_RELEASE_NAME=Chromium OS\n";
+ base::SysInfo::SetChromeOSVersionInfoForTest(kLsbRelease3, base::Time());
+ EXPECT_TRUE(base::SysInfo::IsRunningOnChromeOS());
}
#endif // OS_CHROMEOS
diff --git a/chromium/base/template_util.h b/chromium/base/template_util.h
index e21d4dc7737..f4bf7461327 100644
--- a/chromium/base/template_util.h
+++ b/chromium/base/template_util.h
@@ -28,6 +28,39 @@ typedef integral_constant<bool, false> false_type;
template <class T> struct is_pointer : false_type {};
template <class T> struct is_pointer<T*> : true_type {};
+// Member function pointer detection up to four params. Add more as needed
+// below. This is built-in to C++ 11, and we can remove this when we switch.
+template<typename T>
+struct is_member_function_pointer : false_type {};
+
+template <typename R, typename Z>
+struct is_member_function_pointer<R(Z::*)()> : true_type {};
+template <typename R, typename Z>
+struct is_member_function_pointer<R(Z::*)() const> : true_type {};
+
+template <typename R, typename Z, typename A>
+struct is_member_function_pointer<R(Z::*)(A)> : true_type {};
+template <typename R, typename Z, typename A>
+struct is_member_function_pointer<R(Z::*)(A) const> : true_type {};
+
+template <typename R, typename Z, typename A, typename B>
+struct is_member_function_pointer<R(Z::*)(A, B)> : true_type {};
+template <typename R, typename Z, typename A, typename B>
+struct is_member_function_pointer<R(Z::*)(A, B) const> : true_type {};
+
+template <typename R, typename Z, typename A, typename B, typename C>
+struct is_member_function_pointer<R(Z::*)(A, B, C)> : true_type {};
+template <typename R, typename Z, typename A, typename B, typename C>
+struct is_member_function_pointer<R(Z::*)(A, B, C) const> : true_type {};
+
+template <typename R, typename Z, typename A, typename B, typename C,
+ typename D>
+struct is_member_function_pointer<R(Z::*)(A, B, C, D)> : true_type {};
+template <typename R, typename Z, typename A, typename B, typename C,
+ typename D>
+struct is_member_function_pointer<R(Z::*)(A, B, C, D) const> : true_type {};
+
+
template <class T, class U> struct is_same : public false_type {};
template <class T> struct is_same<T,T> : true_type {};
@@ -39,6 +72,9 @@ template <class T> struct is_non_const_reference : false_type {};
template <class T> struct is_non_const_reference<T&> : true_type {};
template <class T> struct is_non_const_reference<const T&> : false_type {};
+template <class T> struct is_const : false_type {};
+template <class T> struct is_const<const T> : true_type {};
+
template <class T> struct is_void : false_type {};
template <> struct is_void<void> : true_type {};
@@ -103,6 +139,12 @@ struct is_class
sizeof(internal::YesType)> {
};
+template<bool B, class T = void>
+struct enable_if {};
+
+template<class T>
+struct enable_if<true, T> { typedef T type; };
+
} // namespace base
#endif // BASE_TEMPLATE_UTIL_H_
diff --git a/chromium/base/template_util_unittest.cc b/chromium/base/template_util_unittest.cc
index 4cfa3e4fb5a..98ad9389122 100644
--- a/chromium/base/template_util_unittest.cc
+++ b/chromium/base/template_util_unittest.cc
@@ -76,5 +76,55 @@ COMPILE_ASSERT(!is_class<char*>::value, IsClass);
COMPILE_ASSERT(!is_class<int&>::value, IsClass);
COMPILE_ASSERT(!is_class<char[3]>::value, IsClass);
+
+COMPILE_ASSERT(!is_member_function_pointer<int>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<int*>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<void*>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<AStruct>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<AStruct*>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<int(*)(int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<int(*)(int, int)>::value,
+ IsMemberFunctionPointer);
+
+COMPILE_ASSERT(is_member_function_pointer<void (AStruct::*)()>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<void (AStruct::*)(int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int) const>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<int (AStruct::*)(int, int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<
+ int (AStruct::*)(int, int) const>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<
+ int (AStruct::*)(int, int, int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<
+ int (AStruct::*)(int, int, int) const>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<
+ int (AStruct::*)(int, int, int, int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(is_member_function_pointer<
+ int (AStruct::*)(int, int, int, int) const>::value,
+ IsMemberFunctionPointer);
+
+// False because we don't have a specialization for 5 params yet.
+COMPILE_ASSERT(!is_member_function_pointer<
+ int (AStruct::*)(int, int, int, int, int)>::value,
+ IsMemberFunctionPointer);
+COMPILE_ASSERT(!is_member_function_pointer<
+ int (AStruct::*)(int, int, int, int, int) const>::value,
+ IsMemberFunctionPointer);
+
} // namespace
} // namespace base
diff --git a/chromium/base/third_party/dmg_fp/README.chromium b/chromium/base/third_party/dmg_fp/README.chromium
index 10db40838bf..33ab78b9fe7 100644
--- a/chromium/base/third_party/dmg_fp/README.chromium
+++ b/chromium/base/third_party/dmg_fp/README.chromium
@@ -19,4 +19,4 @@ List of changes made to original code:
float_precision_crash.patch and crbug.com/123157
- Fix for 'warning C4703: potentially uninitialized local pointer variable'
in VS2012.
-
+ - Disable optimization on VS2013 pending fix of compiler optimization bug.
diff --git a/chromium/base/third_party/dmg_fp/dtoa.cc b/chromium/base/third_party/dmg_fp/dtoa.cc
index 4eb9f0efd94..b03ccff569f 100644
--- a/chromium/base/third_party/dmg_fp/dtoa.cc
+++ b/chromium/base/third_party/dmg_fp/dtoa.cc
@@ -179,6 +179,12 @@
* 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/dmg_fp/vs2013-optimization.patch b/chromium/base/third_party/dmg_fp/vs2013-optimization.patch
new file mode 100644
index 00000000000..d91b370e51f
--- /dev/null
+++ b/chromium/base/third_party/dmg_fp/vs2013-optimization.patch
@@ -0,0 +1,18 @@
+Index: base/third_party/dmg_fp/dtoa.cc
+diff --git a/base/third_party/dmg_fp/dtoa.cc b/base/third_party/dmg_fp/dtoa.cc
+index 4eb9f0efd94221b3ab95f84554bbc92f112bf973..b03ccff569f9403eb67a95737b0e19740e56ef33 100644
+--- a/base/third_party/dmg_fp/dtoa.cc
++++ b/base/third_party/dmg_fp/dtoa.cc
+@@ -179,6 +179,12 @@
+ * 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/icu/icu_utf.h b/chromium/base/third_party/icu/icu_utf.h
index 9cb12472732..a604e5f5670 100644
--- a/chromium/base/third_party/icu/icu_utf.h
+++ b/chromium/base/third_party/icu/icu_utf.h
@@ -22,6 +22,7 @@
namespace base_icu {
typedef uint32 UChar32;
+typedef uint16 UChar;
typedef int8 UBool;
// General ---------------------------------------------------------------------
@@ -304,7 +305,8 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c
* @return lead surrogate (U+d800..U+dbff) for supplementary
* @stable ICU 2.4
*/
-#define CBU16_LEAD(supplementary) (UChar)(((supplementary)>>10)+0xd7c0)
+#define CBU16_LEAD(supplementary) \
+ (base_icu::UChar)(((supplementary)>>10)+0xd7c0)
/**
* Get the trail surrogate (0xdc00..0xdfff) for a
@@ -313,7 +315,8 @@ UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c
* @return trail surrogate (U+dc00..U+dfff) for supplementary
* @stable ICU 2.4
*/
-#define CBU16_TRAIL(supplementary) (UChar)(((supplementary)&0x3ff)|0xdc00)
+#define CBU16_TRAIL(supplementary) \
+ (base_icu::UChar)(((supplementary)&0x3ff)|0xdc00)
/**
* How many 16-bit code units are used to encode this Unicode code point? (1 or 2)
diff --git a/chromium/base/threading/non_thread_safe.h b/chromium/base/threading/non_thread_safe.h
index cf7a41818da..a27bb0ea0c5 100644
--- a/chromium/base/threading/non_thread_safe.h
+++ b/chromium/base/threading/non_thread_safe.h
@@ -56,7 +56,7 @@ class NonThreadSafeDoNothing {
// }
//
// Note that base::ThreadChecker offers identical functionality to
-// NonThreadSafe, but does not require inheritence. In general, it is preferable
+// NonThreadSafe, but does not require inheritance. In general, it is preferable
// to have a base::ThreadChecker as a member, rather than inherit from
// NonThreadSafe. For more details about when to choose one over the other, see
// the documentation for base::ThreadChecker.
diff --git a/chromium/base/threading/sequenced_worker_pool.h b/chromium/base/threading/sequenced_worker_pool.h
index 1bd275b206d..d3c85e23ae4 100644
--- a/chromium/base/threading/sequenced_worker_pool.h
+++ b/chromium/base/threading/sequenced_worker_pool.h
@@ -69,6 +69,9 @@ class SequencedTaskRunner;
//
// Note that SequencedWorkerPool is RefCountedThreadSafe (inherited
// from TaskRunner).
+//
+// Test-only code should wrap this in a base::SequencedWorkerPoolOwner to avoid
+// memory leaks. See http://crbug.com/273800
class BASE_EXPORT SequencedWorkerPool : public TaskRunner {
public:
// Defines what should happen to a task posted to the worker pool on
diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc
index f0337750356..ae4d3731947 100644
--- a/chromium/base/threading/thread.cc
+++ b/chromium/base/threading/thread.cc
@@ -5,7 +5,6 @@
#include "base/threading/thread.h"
#include "base/bind.h"
-#include "base/debug/alias.h"
#include "base/lazy_instance.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/thread_id_name_manager.h"
@@ -50,6 +49,20 @@ struct Thread::StartupData {
event(false, false) {}
};
+Thread::Options::Options()
+ : message_loop_type(MessageLoop::TYPE_DEFAULT),
+ stack_size(0) {
+}
+
+Thread::Options::Options(MessageLoop::Type type,
+ size_t size)
+ : message_loop_type(type),
+ stack_size(size) {
+}
+
+Thread::Options::~Options() {
+}
+
Thread::Thread(const char* name)
:
#if defined(OS_WIN)
@@ -173,23 +186,16 @@ bool Thread::GetThreadWasQuitProperly() {
void Thread::ThreadMain() {
{
-#if defined(OS_MACOSX)
- // Store the thread name on the stack to debug <http://crbug.com/274705>.
- // End with a byte sequence of <EOT><BEL><NUL> to make it easier to grep in
- // the minidump stack dump.
- const size_t kThreadNameSize = 50;
- char thread_name[kThreadNameSize];
- strncpy(thread_name, name_.c_str(), kThreadNameSize);
- thread_name[kThreadNameSize - 1] = '\0';
- thread_name[kThreadNameSize - 2] = '\7';
- thread_name[kThreadNameSize - 3] = '\3';
- base::debug::Alias(thread_name);
-#endif
-
// The message loop for this thread.
// Allocated on the heap to centralize any leak reports at this line.
- scoped_ptr<MessageLoop> message_loop(
- new MessageLoop(startup_data_->options.message_loop_type));
+ scoped_ptr<MessageLoop> message_loop;
+ if (!startup_data_->options.message_pump_factory.is_null()) {
+ message_loop.reset(
+ new MessageLoop(startup_data_->options.message_pump_factory.Run()));
+ } else {
+ message_loop.reset(
+ new MessageLoop(startup_data_->options.message_loop_type));
+ }
// Complete the initialization of our Thread object.
thread_id_ = PlatformThread::CurrentId();
@@ -231,10 +237,6 @@ void Thread::ThreadMain() {
// We can't receive messages anymore.
message_loop_ = NULL;
-
-#if defined(OS_MACOSX)
- base::debug::Alias(thread_name);
-#endif
}
}
diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h
index 98831b82e5c..99f9dd4a597 100644
--- a/chromium/base/threading/thread.h
+++ b/chromium/base/threading/thread.h
@@ -8,12 +8,16 @@
#include <string>
#include "base/base_export.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/threading/platform_thread.h"
namespace base {
+class MessagePump;
+
// A simple thread abstraction that establishes a MessageLoop on a new thread.
// The consumer uses the MessageLoop of the thread to cause code to execute on
// the thread. When this object is destroyed the thread is terminated. All
@@ -29,14 +33,23 @@ namespace base {
// (3.b) MessageLoop::DestructionObserver::WillDestroyCurrentMessageLoop
class BASE_EXPORT Thread : PlatformThread::Delegate {
public:
- struct Options {
- Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {}
- Options(MessageLoop::Type type, size_t size)
- : message_loop_type(type), stack_size(size) {}
+ struct BASE_EXPORT Options {
+ typedef Callback<scoped_ptr<MessagePump>()> MessagePumpFactory;
+
+ Options();
+ Options(MessageLoop::Type type, size_t size);
+ ~Options();
// Specifies the type of message loop that will be allocated on the thread.
+ // This is ignored if message_pump_factory.is_null() is false.
MessageLoop::Type message_loop_type;
+ // Used to create the MessagePump for the MessageLoop. The callback is Run()
+ // on the thread. If message_pump_factory.is_null(), then a MessagePump
+ // appropriate for |message_loop_type| is created. Setting this forces the
+ // MessageLoop::Type to TYPE_CUSTOM.
+ MessagePumpFactory message_pump_factory;
+
// Specifies the maximum stack size that the thread is allowed to use.
// This does not necessarily correspond to the thread's initial stack size.
// A value of 0 indicates that the default maximum should be used.
diff --git a/chromium/base/threading/thread_checker.h b/chromium/base/threading/thread_checker.h
index 5a8ef2261d7..a2ab3fe4fab 100644
--- a/chromium/base/threading/thread_checker.h
+++ b/chromium/base/threading/thread_checker.h
@@ -46,7 +46,7 @@ class ThreadCheckerDoNothing {
//
// While inheriting from base::NonThreadSafe may give a clear indication about
// the thread-safety of a class, it may also lead to violations of the style
-// guide with regard to multiple inheritence. The choice between having a
+// guide with regard to multiple inheritance. The choice between having a
// ThreadChecker member and inheriting from base::NonThreadSafe should be based
// on whether:
// - Derived classes need to know the thread they belong to, as opposed to
diff --git a/chromium/base/threading/thread_local.h b/chromium/base/threading/thread_local.h
index 6561420758c..b13be1ab467 100644
--- a/chromium/base/threading/thread_local.h
+++ b/chromium/base/threading/thread_local.h
@@ -56,7 +56,6 @@
#endif
namespace base {
-
namespace internal {
// Helper functions that abstract the cross-platform APIs. Do not use directly.
@@ -67,10 +66,10 @@ struct BASE_EXPORT ThreadLocalPlatform {
typedef pthread_key_t SlotType;
#endif
- static void AllocateSlot(SlotType& slot);
- static void FreeSlot(SlotType& slot);
- static void* GetValueFromSlot(SlotType& slot);
- static void SetValueInSlot(SlotType& slot, void* value);
+ static void AllocateSlot(SlotType* slot);
+ static void FreeSlot(SlotType slot);
+ static void* GetValueFromSlot(SlotType slot);
+ static void SetValueInSlot(SlotType slot, void* value);
};
} // namespace internal
@@ -79,7 +78,7 @@ template <typename Type>
class ThreadLocalPointer {
public:
ThreadLocalPointer() : slot_() {
- internal::ThreadLocalPlatform::AllocateSlot(slot_);
+ internal::ThreadLocalPlatform::AllocateSlot(&slot_);
}
~ThreadLocalPointer() {
@@ -106,8 +105,8 @@ class ThreadLocalPointer {
class ThreadLocalBoolean {
public:
- ThreadLocalBoolean() { }
- ~ThreadLocalBoolean() { }
+ ThreadLocalBoolean() {}
+ ~ThreadLocalBoolean() {}
bool Get() {
return tlp_.Get() != NULL;
diff --git a/chromium/base/threading/thread_local_posix.cc b/chromium/base/threading/thread_local_posix.cc
index 49510064e75..526a66d0629 100644
--- a/chromium/base/threading/thread_local_posix.cc
+++ b/chromium/base/threading/thread_local_posix.cc
@@ -9,32 +9,30 @@
#include "base/logging.h"
namespace base {
-
namespace internal {
// static
-void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
- int error = pthread_key_create(&slot, NULL);
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ int error = pthread_key_create(slot, NULL);
CHECK_EQ(error, 0);
}
// static
-void ThreadLocalPlatform::FreeSlot(SlotType& slot) {
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
int error = pthread_key_delete(slot);
DCHECK_EQ(0, error);
}
// static
-void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
return pthread_getspecific(slot);
}
// static
-void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
int error = pthread_setspecific(slot, value);
DCHECK_EQ(error, 0);
}
} // namespace internal
-
} // namespace base
diff --git a/chromium/base/threading/thread_local_win.cc b/chromium/base/threading/thread_local_win.cc
index 56d3a3ac7f2..1c74e421387 100644
--- a/chromium/base/threading/thread_local_win.cc
+++ b/chromium/base/threading/thread_local_win.cc
@@ -9,34 +9,32 @@
#include "base/logging.h"
namespace base {
-
namespace internal {
// static
-void ThreadLocalPlatform::AllocateSlot(SlotType& slot) {
- slot = TlsAlloc();
- CHECK_NE(slot, TLS_OUT_OF_INDEXES);
+void ThreadLocalPlatform::AllocateSlot(SlotType* slot) {
+ *slot = TlsAlloc();
+ CHECK_NE(*slot, TLS_OUT_OF_INDEXES);
}
// static
-void ThreadLocalPlatform::FreeSlot(SlotType& slot) {
+void ThreadLocalPlatform::FreeSlot(SlotType slot) {
if (!TlsFree(slot)) {
NOTREACHED() << "Failed to deallocate tls slot with TlsFree().";
}
}
// static
-void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) {
+void* ThreadLocalPlatform::GetValueFromSlot(SlotType slot) {
return TlsGetValue(slot);
}
// static
-void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) {
+void ThreadLocalPlatform::SetValueInSlot(SlotType slot, void* value) {
if (!TlsSetValue(slot, value)) {
LOG(FATAL) << "Failed to TlsSetValue().";
}
}
} // namespace internal
-
} // namespace base
diff --git a/chromium/base/threading/watchdog.cc b/chromium/base/threading/watchdog.cc
index a18efecc5f6..82c187564b1 100644
--- a/chromium/base/threading/watchdog.cc
+++ b/chromium/base/threading/watchdog.cc
@@ -20,14 +20,18 @@ namespace {
// Without this safety net, any alarm will typically trigger a host of follow
// on alarms from callers that specify old times.
-// Lock for access of static data...
-LazyInstance<Lock>::Leaky g_static_lock = LAZY_INSTANCE_INITIALIZER;
+struct StaticData {
+ // Lock for access of static data...
+ Lock lock;
-// When did we last alarm and get stuck (for a while) in a debugger?
-TimeTicks g_last_debugged_alarm_time;
+ // When did we last alarm and get stuck (for a while) in a debugger?
+ TimeTicks last_debugged_alarm_time;
-// How long did we sit on a break in the debugger?
-TimeDelta g_last_debugged_alarm_delay;
+ // How long did we sit on a break in the debugger?
+ TimeDelta last_debugged_alarm_delay;
+};
+
+LazyInstance<StaticData>::Leaky g_static_data = LAZY_INSTANCE_INITIALIZER;
} // namespace
@@ -115,6 +119,7 @@ void Watchdog::Alarm() {
void Watchdog::ThreadDelegate::ThreadMain() {
SetThreadName();
TimeDelta remaining_duration;
+ StaticData* static_data = g_static_data.Pointer();
while (1) {
AutoLock lock(watchdog_->lock_);
while (DISARMED == watchdog_->state_)
@@ -134,12 +139,12 @@ void Watchdog::ThreadDelegate::ThreadMain() {
// We overslept, so this seems like a real alarm.
// Watch out for a user that stopped the debugger on a different alarm!
{
- AutoLock static_lock(*g_static_lock.Pointer());
- if (g_last_debugged_alarm_time > watchdog_->start_time_) {
+ AutoLock static_lock(static_data->lock);
+ if (static_data->last_debugged_alarm_time > watchdog_->start_time_) {
// False alarm: we started our clock before the debugger break (last
// alarm time).
- watchdog_->start_time_ += g_last_debugged_alarm_delay;
- if (g_last_debugged_alarm_time > watchdog_->start_time_)
+ watchdog_->start_time_ += static_data->last_debugged_alarm_delay;
+ if (static_data->last_debugged_alarm_time > watchdog_->start_time_)
// Too many alarms must have taken place.
watchdog_->state_ = DISARMED;
continue;
@@ -155,10 +160,10 @@ void Watchdog::ThreadDelegate::ThreadMain() {
if (last_alarm_delay <= TimeDelta::FromMilliseconds(2))
continue;
// Ignore race of two alarms/breaks going off at roughly the same time.
- AutoLock static_lock(*g_static_lock.Pointer());
+ AutoLock static_lock(static_data->lock);
// This was a real debugger break.
- g_last_debugged_alarm_time = last_alarm_time;
- g_last_debugged_alarm_delay = last_alarm_delay;
+ static_data->last_debugged_alarm_time = last_alarm_time;
+ static_data->last_debugged_alarm_delay = last_alarm_delay;
}
}
@@ -170,9 +175,10 @@ void Watchdog::ThreadDelegate::SetThreadName() const {
// static
void Watchdog::ResetStaticData() {
- AutoLock lock(*g_static_lock.Pointer());
- g_last_debugged_alarm_time = TimeTicks();
- g_last_debugged_alarm_delay = TimeDelta();
+ StaticData* static_data = g_static_data.Pointer();
+ AutoLock lock(static_data->lock);
+ static_data->last_debugged_alarm_time = TimeTicks();
+ static_data->last_debugged_alarm_delay = TimeDelta();
}
} // namespace base
diff --git a/chromium/base/threading/worker_pool_posix_unittest.cc b/chromium/base/threading/worker_pool_posix_unittest.cc
index 49f6570aa4e..862ffddcf89 100644
--- a/chromium/base/threading/worker_pool_posix_unittest.cc
+++ b/chromium/base/threading/worker_pool_posix_unittest.cc
@@ -160,7 +160,6 @@ TEST_F(PosixDynamicThreadPoolTest, Basic) {
EXPECT_EQ(1U, unique_threads_.size()) <<
"There should be only one thread allocated for one task.";
- EXPECT_EQ(1, peer_.num_idle_threads());
EXPECT_EQ(1, counter_);
}
diff --git a/chromium/base/time/time.cc b/chromium/base/time/time.cc
index a9e4b1cf90a..9f3c53d6695 100644
--- a/chromium/base/time/time.cc
+++ b/chromium/base/time/time.cc
@@ -8,6 +8,7 @@
#include <ostream>
#include "base/float_util.h"
+#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/third_party/nspr/prtime.h"
#include "base/third_party/nspr/prtypes.h"
@@ -189,6 +190,29 @@ bool Time::FromStringInternal(const char* time_string,
return true;
}
+// Local helper class to hold the conversion from Time to TickTime at the
+// time of the Unix epoch.
+class UnixEpochSingleton {
+ public:
+ UnixEpochSingleton()
+ : unix_epoch_(TimeTicks::Now() - (Time::Now() - Time::UnixEpoch())) {}
+
+ TimeTicks unix_epoch() const { return unix_epoch_; }
+
+ private:
+ const TimeTicks unix_epoch_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnixEpochSingleton);
+};
+
+static LazyInstance<UnixEpochSingleton>::Leaky
+ leaky_unix_epoch_singleton_instance = LAZY_INSTANCE_INITIALIZER;
+
+// Static
+TimeTicks TimeTicks::UnixEpoch() {
+ return leaky_unix_epoch_singleton_instance.Get().unix_epoch();
+}
+
// Time::Exploded -------------------------------------------------------------
inline bool is_in_range(int value, int lo, int hi) {
diff --git a/chromium/base/time/time.h b/chromium/base/time/time.h
index a236a3f09a0..40566b92317 100644
--- a/chromium/base/time/time.h
+++ b/chromium/base/time/time.h
@@ -544,9 +544,12 @@ class BASE_EXPORT TimeTicks {
// SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED.
static TimeTicks HighResNow();
+ static bool IsHighResNowFastAndReliable();
+
// Returns true if ThreadNow() is supported on this system.
static bool IsThreadNowSupported() {
-#if defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)
+#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
+ (defined(OS_MACOSX) && !defined(OS_IOS)) || defined(OS_ANDROID)
return true;
#else
return false;
@@ -602,6 +605,14 @@ class BASE_EXPORT TimeTicks {
return TimeTicks(ticks);
}
+ // Get the TimeTick value at the time of the UnixEpoch. This is useful when
+ // you need to relate the value of TimeTicks to a real time and date.
+ // Note: Upon first invocation, this function takes a snapshot of the realtime
+ // clock to establish a reference point. This function will return the same
+ // value for the duration of the application, but will be different in future
+ // application runs.
+ static TimeTicks UnixEpoch();
+
// Returns the internal numeric value of the TimeTicks object.
// For serializing, use FromInternalValue to reconstitute.
int64 ToInternalValue() const {
diff --git a/chromium/base/time/time_mac.cc b/chromium/base/time/time_mac.cc
index da46aa7ee51..2388ddc6e69 100644
--- a/chromium/base/time/time_mac.cc
+++ b/chromium/base/time/time_mac.cc
@@ -6,6 +6,7 @@
#include <CoreFoundation/CFDate.h>
#include <CoreFoundation/CFTimeZone.h>
+#include <mach/mach.h>
#include <mach/mach_time.h>
#include <sys/sysctl.h>
#include <sys/time.h>
@@ -15,6 +16,7 @@
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_mach_port.h"
namespace {
@@ -64,6 +66,33 @@ uint64_t ComputeCurrentTicks() {
#endif // defined(OS_IOS)
}
+uint64_t ComputeThreadTicks() {
+#if defined(OS_IOS)
+ NOTREACHED();
+ return 0;
+#else
+ base::mac::ScopedMachPort 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) {
+ DLOG(ERROR) << "Failed to get mach_thread_self()";
+ return 0;
+ }
+
+ kern_return_t kr = thread_info(
+ thread,
+ THREAD_BASIC_INFO,
+ reinterpret_cast<thread_info_t>(&thread_info_data),
+ &thread_info_count);
+ DCHECK_EQ(KERN_SUCCESS, kr);
+
+ return (thread_info_data.user_time.seconds *
+ base::Time::kMicrosecondsPerSecond) +
+ thread_info_data.user_time.microseconds;
+#endif // defined(OS_IOS)
+}
+
} // namespace
namespace base {
@@ -85,8 +114,6 @@ namespace base {
// irb(main):011:0> Time.at(-11644473600).getutc()
// => Mon Jan 01 00:00:00 UTC 1601
static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
-static const int64 kWindowsEpochDeltaMilliseconds =
- kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
// static
const int64 Time::kWindowsEpochDeltaMicroseconds =
@@ -192,9 +219,13 @@ TimeTicks TimeTicks::HighResNow() {
}
// static
+bool TimeTicks::IsHighResNowFastAndReliable() {
+ return true;
+}
+
+// static
TimeTicks TimeTicks::ThreadNow() {
- NOTREACHED();
- return TimeTicks();
+ return TimeTicks(ComputeThreadTicks());
}
// static
diff --git a/chromium/base/time/time_posix.cc b/chromium/base/time/time_posix.cc
index dc551b0dab6..3378038fe44 100644
--- a/chromium/base/time/time_posix.cc
+++ b/chromium/base/time/time_posix.cc
@@ -28,6 +28,7 @@
namespace {
+#if !defined(OS_MACOSX)
// 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
@@ -66,7 +67,6 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) {
}
#endif // OS_ANDROID
-#if !defined(OS_MACOSX)
// Helper function to get results from clock_gettime() as TimeTicks object.
// Minimum requirement is MONOTONIC_CLOCK to be supported on the system.
// FreeBSD 6 has CLOCK_MONOTONIC but defines _POSIX_MONOTONIC_CLOCK to -1.
@@ -124,8 +124,6 @@ struct timespec TimeDelta::ToTimeSpec() const {
// irb(main):011:0> Time.at(-11644473600).getutc()
// => Mon Jan 01 00:00:00 UTC 1601
static const int64 kWindowsEpochDeltaSeconds = GG_INT64_C(11644473600);
-static const int64 kWindowsEpochDeltaMilliseconds =
- kWindowsEpochDeltaSeconds * Time::kMillisecondsPerSecond;
// static
const int64 Time::kWindowsEpochDeltaMicroseconds =
@@ -307,8 +305,14 @@ TimeTicks TimeTicks::HighResNow() {
}
// static
+bool TimeTicks::IsHighResNowFastAndReliable() {
+ return true;
+}
+
+// static
TimeTicks TimeTicks::ThreadNow() {
-#if defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)
+#if (defined(_POSIX_THREAD_CPUTIME) && (_POSIX_THREAD_CPUTIME >= 0)) || \
+ defined(OS_ANDROID)
return ClockNow(CLOCK_THREAD_CPUTIME_ID);
#else
NOTREACHED();
diff --git a/chromium/base/time/time_unittest.cc b/chromium/base/time/time_unittest.cc
index 81a3358f08c..e78a61cf4bd 100644
--- a/chromium/base/time/time_unittest.cc
+++ b/chromium/base/time/time_unittest.cc
@@ -639,12 +639,17 @@ TEST(TimeTicks, ThreadNow) {
if (TimeTicks::IsThreadNowSupported()) {
TimeTicks begin = TimeTicks::Now();
TimeTicks begin_thread = TimeTicks::ThreadNow();
- // Sleep for 10 milliseconds to get the thread de-scheduled
+ // Make sure that ThreadNow value is non-zero.
+ EXPECT_GT(begin_thread, TimeTicks());
+ // Sleep for 10 milliseconds to get the thread de-scheduled.
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
TimeTicks end_thread = TimeTicks::ThreadNow();
TimeTicks end = TimeTicks::Now();
TimeDelta delta = end - begin;
TimeDelta delta_thread = end_thread - begin_thread;
+ // Make sure that some thread time have elapsed.
+ EXPECT_GT(delta_thread.InMicroseconds(), 0);
+ // But the thread time is at least 9ms less than clock time.
TimeDelta difference = delta - delta_thread;
EXPECT_GE(difference.InMicroseconds(), 9000);
}
diff --git a/chromium/base/time/time_win.cc b/chromium/base/time/time_win.cc
index f35f735bfbd..ef8d29a92cb 100644
--- a/chromium/base/time/time_win.cc
+++ b/chromium/base/time/time_win.cc
@@ -489,6 +489,11 @@ TimeTicks TimeTicks::HighResNow() {
}
// static
+bool TimeTicks::IsHighResNowFastAndReliable() {
+ return CPUReliablySupportsHighResTime();
+}
+
+// static
TimeTicks TimeTicks::ThreadNow() {
NOTREACHED();
return TimeTicks();
diff --git a/chromium/base/timer/elapsed_timer.cc b/chromium/base/timer/elapsed_timer.cc
new file mode 100644
index 00000000000..f2a2f7113e8
--- /dev/null
+++ b/chromium/base/timer/elapsed_timer.cc
@@ -0,0 +1,17 @@
+// 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/timer/elapsed_timer.h"
+
+namespace base {
+
+ElapsedTimer::ElapsedTimer() {
+ begin_ = TimeTicks::Now();
+}
+
+TimeDelta ElapsedTimer::Elapsed() const {
+ return TimeTicks::Now() - begin_;
+}
+
+} // namespace base
diff --git a/chromium/base/timer/elapsed_timer.h b/chromium/base/timer/elapsed_timer.h
new file mode 100644
index 00000000000..3ea2bfadf38
--- /dev/null
+++ b/chromium/base/timer/elapsed_timer.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_TIMER_ELAPSED_TIMER_H_
+#define BASE_TIMER_ELAPSED_TIMER_H_
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+#include "base/time/time.h"
+
+namespace base {
+
+// A simple wrapper around TimeTicks::Now().
+class BASE_EXPORT ElapsedTimer {
+ public:
+ ElapsedTimer();
+
+ // Returns the time elapsed since object construction.
+ TimeDelta Elapsed() const;
+
+ private:
+ TimeTicks begin_;
+
+ DISALLOW_COPY_AND_ASSIGN(ElapsedTimer);
+};
+
+} // namespace base
+
+#endif // BASE_TIMER_ELAPSED_TIMER_H_
diff --git a/chromium/base/timer/timer.cc b/chromium/base/timer/timer.cc
index aafc01e1304..1c4b10c6de5 100644
--- a/chromium/base/timer/timer.cc
+++ b/chromium/base/timer/timer.cc
@@ -108,11 +108,14 @@ void Timer::Reset() {
}
// Set the new desired_run_time_.
- desired_run_time_ = TimeTicks::Now() + delay_;
+ if (delay_ > TimeDelta::FromMicroseconds(0))
+ desired_run_time_ = TimeTicks::Now() + delay_;
+ else
+ desired_run_time_ = TimeTicks();
// We can use the existing scheduled task if it arrives before the new
// desired_run_time_.
- if (desired_run_time_ > scheduled_run_time_) {
+ if (desired_run_time_ >= scheduled_run_time_) {
is_running_ = true;
return;
}
@@ -134,10 +137,16 @@ void Timer::PostNewScheduledTask(TimeDelta delay) {
DCHECK(scheduled_task_ == NULL);
is_running_ = true;
scheduled_task_ = new BaseTimerTaskInternal(this);
- ThreadTaskRunnerHandle::Get()->PostDelayedTask(posted_from_,
- base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
- delay);
- scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
+ if (delay > TimeDelta::FromMicroseconds(0)) {
+ ThreadTaskRunnerHandle::Get()->PostDelayedTask(posted_from_,
+ base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)),
+ delay);
+ scheduled_run_time_ = desired_run_time_ = TimeTicks::Now() + delay;
+ } else {
+ ThreadTaskRunnerHandle::Get()->PostTask(posted_from_,
+ base::Bind(&BaseTimerTaskInternal::Run, base::Owned(scheduled_task_)));
+ scheduled_run_time_ = desired_run_time_ = TimeTicks();
+ }
// Remember the thread ID that posts the first task -- this will be verified
// later when the task is abandoned to detect misuse from multiple threads.
if (!thread_id_)
diff --git a/chromium/base/timer/timer.h b/chromium/base/timer/timer.h
index f952d414abf..71a7ae8888a 100644
--- a/chromium/base/timer/timer.h
+++ b/chromium/base/timer/timer.h
@@ -148,7 +148,8 @@ class BASE_EXPORT Timer {
base::Closure user_task_;
// The estimated time that the MessageLoop will run the scheduled_task_ that
- // will call RunScheduledTask().
+ // will call RunScheduledTask(). This time can be a "zero" TimeTicks if the
+ // task must be run immediately.
TimeTicks scheduled_run_time_;
// The desired run time of user_task_. The user may update this at any time,
@@ -156,7 +157,8 @@ class BASE_EXPORT Timer {
// greater than scheduled_run_time_, a continuation task will be posted to
// wait for the remaining time. This allows us to reuse the pending task so as
// not to flood the MessageLoop with orphaned tasks when the user code
- // excessively Stops and Starts the timer.
+ // excessively Stops and Starts the timer. This time can be a "zero" TimeTicks
+ // if the task must be run immediately.
TimeTicks desired_run_time_;
// Thread ID of current MessageLoop for verifying single-threaded usage.
diff --git a/chromium/base/timer/timer_unittest.cc b/chromium/base/timer/timer_unittest.cc
index 417d258f2ec..728e861b537 100644
--- a/chromium/base/timer/timer_unittest.cc
+++ b/chromium/base/timer/timer_unittest.cc
@@ -64,23 +64,24 @@ class OneShotSelfDeletingTimerTester {
class RepeatingTimerTester {
public:
- explicit RepeatingTimerTester(bool* did_run)
- : did_run_(did_run), counter_(10) {
+ explicit RepeatingTimerTester(bool* did_run, const TimeDelta& delay)
+ : did_run_(did_run), counter_(10), delay_(delay) {
}
void Start() {
- timer_.Start(FROM_HERE, TimeDelta::FromMilliseconds(10), this,
- &RepeatingTimerTester::Run);
+ timer_.Start(FROM_HERE, delay_, this, &RepeatingTimerTester::Run);
}
private:
void Run() {
if (--counter_ == 0) {
*did_run_ = true;
+ timer_.Stop();
base::MessageLoop::current()->QuitWhenIdle();
}
}
bool* did_run_;
int counter_;
+ TimeDelta delay_;
base::RepeatingTimer<RepeatingTimerTester> timer_;
};
@@ -131,11 +132,12 @@ void RunTest_OneShotSelfDeletingTimer(
EXPECT_TRUE(did_run);
}
-void RunTest_RepeatingTimer(base::MessageLoop::Type message_loop_type) {
+void RunTest_RepeatingTimer(base::MessageLoop::Type message_loop_type,
+ const TimeDelta& delay) {
base::MessageLoop loop(message_loop_type);
bool did_run = false;
- RepeatingTimerTester f(&did_run);
+ RepeatingTimerTester f(&did_run, delay);
f.Start();
base::MessageLoop::current()->Run();
@@ -143,11 +145,12 @@ void RunTest_RepeatingTimer(base::MessageLoop::Type message_loop_type) {
EXPECT_TRUE(did_run);
}
-void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type) {
+void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type,
+ const TimeDelta& delay) {
base::MessageLoop loop(message_loop_type);
bool did_run_a = false;
- RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a);
+ RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a, delay);
// This should run before the timer expires.
base::MessageLoop::current()->DeleteSoon(FROM_HERE, a);
@@ -156,7 +159,7 @@ void RunTest_RepeatingTimer_Cancel(base::MessageLoop::Type message_loop_type) {
a->Start();
bool did_run_b = false;
- RepeatingTimerTester b(&did_run_b);
+ RepeatingTimerTester b(&did_run_b, delay);
b.Start();
base::MessageLoop::current()->Run();
@@ -309,13 +312,29 @@ TEST(TimerTest, OneShotSelfDeletingTimer) {
TEST(TimerTest, RepeatingTimer) {
for (int i = 0; i < kNumTestingMessageLoops; i++) {
- RunTest_RepeatingTimer(testing_message_loops[i]);
+ RunTest_RepeatingTimer(testing_message_loops[i],
+ TimeDelta::FromMilliseconds(10));
}
}
TEST(TimerTest, RepeatingTimer_Cancel) {
for (int i = 0; i < kNumTestingMessageLoops; i++) {
- RunTest_RepeatingTimer_Cancel(testing_message_loops[i]);
+ RunTest_RepeatingTimer_Cancel(testing_message_loops[i],
+ TimeDelta::FromMilliseconds(10));
+ }
+}
+
+TEST(TimerTest, RepeatingTimerZeroDelay) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer(testing_message_loops[i],
+ TimeDelta::FromMilliseconds(0));
+ }
+}
+
+TEST(TimerTest, RepeatingTimerZeroDelay_Cancel) {
+ for (int i = 0; i < kNumTestingMessageLoops; i++) {
+ RunTest_RepeatingTimer_Cancel(testing_message_loops[i],
+ TimeDelta::FromMilliseconds(0));
}
}
diff --git a/chromium/base/tracked_objects.cc b/chromium/base/tracked_objects.cc
index 0847d5f9163..0db7094c498 100644
--- a/chromium/base/tracked_objects.cc
+++ b/chromium/base/tracked_objects.cc
@@ -7,6 +7,9 @@
#include <limits.h>
#include <stdlib.h>
+#include "base/atomicops.h"
+#include "base/base_switches.h"
+#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/debug/leak_annotations.h"
#include "base/logging.h"
@@ -51,6 +54,32 @@ const ThreadData::Status kInitialStartupState =
// problem with its presence).
static const bool kAllowAlternateTimeSourceHandling = true;
+inline bool IsProfilerTimingEnabled() {
+ enum {
+ UNDEFINED_TIMING,
+ ENABLED_TIMING,
+ DISABLED_TIMING,
+ };
+ static base::subtle::Atomic32 timing_enabled = UNDEFINED_TIMING;
+ // Reading |timing_enabled| is done without barrier because multiple
+ // initialization is not an issue while the barrier can be relatively costly
+ // given that this method is sometimes called in a tight loop.
+ base::subtle::Atomic32 current_timing_enabled =
+ base::subtle::NoBarrier_Load(&timing_enabled);
+ if (current_timing_enabled == UNDEFINED_TIMING) {
+ if (!CommandLine::InitializedForCurrentProcess())
+ return true;
+ current_timing_enabled =
+ (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kProfilerTiming) ==
+ switches::kProfilerTimingDisabledValue)
+ ? DISABLED_TIMING
+ : ENABLED_TIMING;
+ base::subtle::NoBarrier_Store(&timing_enabled, current_timing_enabled);
+ }
+ return current_timing_enabled == ENABLED_TIMING;
+}
+
} // namespace
//------------------------------------------------------------------------------
@@ -754,7 +783,7 @@ void ThreadData::SetAlternateTimeSource(NowFunction* now_function) {
TrackedTime ThreadData::Now() {
if (kAllowAlternateTimeSourceHandling && now_function_)
return TrackedTime::FromMilliseconds((*now_function_)());
- if (kTrackAllTaskObjects && TrackingStatus())
+ if (kTrackAllTaskObjects && IsProfilerTimingEnabled() && TrackingStatus())
return TrackedTime::Now();
return TrackedTime(); // Super fast when disabled, or not compiled.
}
diff --git a/chromium/base/values.cc b/chromium/base/values.cc
index 2d3984e0d42..6068556bb3d 100644
--- a/chromium/base/values.cc
+++ b/chromium/base/values.cc
@@ -462,13 +462,11 @@ void DictionaryValue::SetStringWithoutPathExpansion(
SetWithoutPathExpansion(path, CreateStringValue(in_value));
}
-bool DictionaryValue::Get(
- const std::string& path, const Value** out_value) const {
+bool DictionaryValue::Get(const std::string& path,
+ const Value** out_value) const {
DCHECK(IsStringUTF8(path));
-// LOG(WARNING) << "\n1\n";
std::string current_path(path);
const DictionaryValue* current_dictionary = this;
-// LOG(WARNING) << "\n2\n";
for (size_t delimiter_position = current_path.find('.');
delimiter_position != std::string::npos;
delimiter_position = current_path.find('.')) {
@@ -480,7 +478,6 @@ bool DictionaryValue::Get(
current_dictionary = child_dictionary;
current_path.erase(0, delimiter_position + 1);
}
-// LOG(WARNING) << "\n3\n";
return current_dictionary->GetWithoutPathExpansion(current_path, out_value);
}
@@ -755,6 +752,26 @@ bool DictionaryValue::RemoveWithoutPathExpansion(const std::string& key,
return true;
}
+bool DictionaryValue::RemovePath(const std::string& path,
+ scoped_ptr<Value>* out_value) {
+ bool result = false;
+ size_t delimiter_position = path.find('.');
+
+ if (delimiter_position == std::string::npos)
+ return RemoveWithoutPathExpansion(path, out_value);
+
+ const std::string subdict_path = path.substr(0, delimiter_position);
+ DictionaryValue* subdict = NULL;
+ if (!GetDictionary(subdict_path, &subdict))
+ return false;
+ result = subdict->RemovePath(path.substr(delimiter_position + 1),
+ out_value);
+ if (result && subdict->empty())
+ RemoveWithoutPathExpansion(subdict_path, NULL);
+
+ return result;
+}
+
DictionaryValue* DictionaryValue::DeepCopyWithoutEmptyChildren() const {
Value* copy = CopyWithoutEmptyChildren(this);
return copy ? static_cast<DictionaryValue*>(copy) : new DictionaryValue;
@@ -785,6 +802,8 @@ DictionaryValue::Iterator::Iterator(const DictionaryValue& target)
: target_(target),
it_(target.dictionary_.begin()) {}
+DictionaryValue::Iterator::~Iterator() {}
+
DictionaryValue* DictionaryValue::DeepCopy() const {
DictionaryValue* result = new DictionaryValue;
diff --git a/chromium/base/values.h b/chromium/base/values.h
index 3bc1f8bbafa..65121808e64 100644
--- a/chromium/base/values.h
+++ b/chromium/base/values.h
@@ -324,6 +324,11 @@ class BASE_EXPORT DictionaryValue : public Value {
virtual bool RemoveWithoutPathExpansion(const std::string& key,
scoped_ptr<Value>* out_value);
+ // Removes a path, clearing out all dictionaries on |path| that remain empty
+ // after removing the value at |path|.
+ virtual bool RemovePath(const std::string& path,
+ scoped_ptr<Value>* out_value);
+
// Makes a copy of |this| but doesn't include empty dictionaries and lists in
// the copy. This never returns NULL, even if |this| itself is empty.
DictionaryValue* DeepCopyWithoutEmptyChildren() const;
@@ -343,6 +348,7 @@ class BASE_EXPORT DictionaryValue : public Value {
class BASE_EXPORT Iterator {
public:
explicit Iterator(const DictionaryValue& target);
+ ~Iterator();
bool IsAtEnd() const { return it_ == target_.dictionary_.end(); }
void Advance() { ++it_; }
diff --git a/chromium/base/values_unittest.cc b/chromium/base/values_unittest.cc
index 733c485d2b1..70acdfd6966 100644
--- a/chromium/base/values_unittest.cc
+++ b/chromium/base/values_unittest.cc
@@ -323,6 +323,31 @@ TEST(ValuesTest, DictionaryWithoutPathExpansion) {
EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
}
+TEST(ValuesTest, DictionaryRemovePath) {
+ DictionaryValue dict;
+ dict.Set("a.long.way.down", Value::CreateIntegerValue(1));
+ dict.Set("a.long.key.path", Value::CreateBooleanValue(true));
+
+ scoped_ptr<Value> removed_item;
+ EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item));
+ ASSERT_TRUE(removed_item);
+ EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_INTEGER));
+ EXPECT_FALSE(dict.HasKey("a.long.way.down"));
+ EXPECT_FALSE(dict.HasKey("a.long.way"));
+ EXPECT_TRUE(dict.Get("a.long.key.path", NULL));
+
+ removed_item.reset();
+ EXPECT_FALSE(dict.RemovePath("a.long.way.down", &removed_item));
+ EXPECT_FALSE(removed_item);
+ EXPECT_TRUE(dict.Get("a.long.key.path", NULL));
+
+ removed_item.reset();
+ EXPECT_TRUE(dict.RemovePath("a.long.key.path", &removed_item));
+ ASSERT_TRUE(removed_item);
+ EXPECT_TRUE(removed_item->IsType(base::Value::TYPE_BOOLEAN));
+ EXPECT_TRUE(dict.empty());
+}
+
TEST(ValuesTest, DeepCopy) {
DictionaryValue original_dict;
Value* original_null = Value::CreateNullValue();
diff --git a/chromium/base/win/event_trace_controller_unittest.cc b/chromium/base/win/event_trace_controller_unittest.cc
index 16bf1e134a5..4d23eddc3bb 100644
--- a/chromium/base/win/event_trace_controller_unittest.cc
+++ b/chromium/base/win/event_trace_controller_unittest.cc
@@ -164,7 +164,7 @@ TEST_F(EtwTraceControllerTest, StartFileSession) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
FilePath temp;
- ASSERT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir.path(), &temp));
+ ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &temp));
EtwTraceController controller;
HRESULT hr = controller.StartFileSession(session_name_.c_str(),
diff --git a/chromium/base/win/scoped_co_mem.h b/chromium/base/win/scoped_co_mem.h
index 572999a26c1..fc85114828f 100644
--- a/chromium/base/win/scoped_co_mem.h
+++ b/chromium/base/win/scoped_co_mem.h
@@ -52,6 +52,10 @@ class ScopedCoMem {
mem_ptr_ = ptr;
}
+ T* get() const {
+ return mem_ptr_;
+ }
+
private:
T* mem_ptr_;
diff --git a/chromium/base/win/scoped_handle.h b/chromium/base/win/scoped_handle.h
index d236a70beb4..0d038e010bd 100644
--- a/chromium/base/win/scoped_handle.h
+++ b/chromium/base/win/scoped_handle.h
@@ -13,21 +13,18 @@
#include "base/logging.h"
#include "base/move.h"
-namespace base {
-namespace win {
-
// TODO(rvargas): remove this with the rest of the verifier.
#if defined(COMPILER_MSVC)
-// MSDN says to #include <intrin.h>, but that breaks the VS2005 build.
-extern "C" {
- void* _ReturnAddress();
-}
+#include <intrin.h>
#define BASE_WIN_GET_CALLER _ReturnAddress()
#elif defined(COMPILER_GCC)
#define BASE_WIN_GET_CALLER __builtin_extract_return_addr(\\
__builtin_return_address(0))
#endif
+namespace base {
+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:
@@ -42,22 +39,6 @@ class GenericScopedHandle {
public:
typedef typename Traits::Handle Handle;
- // Helper object to contain the effect of Receive() to the function that needs
- // a pointer, and allow proper tracking of the handle.
- class Receiver {
- public:
- explicit Receiver(GenericScopedHandle* owner)
- : handle_(Traits::NullHandle()),
- owner_(owner) {}
- ~Receiver() { owner_->Set(handle_); }
-
- operator Handle*() { return &handle_; }
-
- private:
- Handle handle_;
- GenericScopedHandle* owner_;
- };
-
GenericScopedHandle() : handle_(Traits::NullHandle()) {}
explicit GenericScopedHandle(Handle handle) : handle_(Traits::NullHandle()) {
@@ -105,16 +86,6 @@ class GenericScopedHandle {
return handle_;
}
- // This method is intended to be used with functions that require a pointer to
- // a destination handle, like so:
- // void CreateRequiredHandle(Handle* out_handle);
- // ScopedHandle a;
- // CreateRequiredHandle(a.Receive());
- Receiver Receive() {
- DCHECK(!Traits::IsHandleValid(handle_)) << "Handle must be NULL";
- return Receiver(this);
- }
-
// Transfers ownership away from this object.
Handle Take() {
Handle temp = handle_;
diff --git a/chromium/base/win/scoped_handle_unittest.cc b/chromium/base/win/scoped_handle_unittest.cc
deleted file mode 100644
index ee2a551d63c..00000000000
--- a/chromium/base/win/scoped_handle_unittest.cc
+++ /dev/null
@@ -1,31 +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/scoped_handle.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-void CreateHandle(int value, HANDLE* result) {
- *result = reinterpret_cast<HANDLE>(value);
-}
-
-TEST(ScopedHandleTest, Receive) {
- base::win::ScopedHandle handle;
- int value = 51;
-
- {
- // This is not really the expected use case, but it is a very explicit test.
- base::win::ScopedHandle::Receiver a = handle.Receive();
- HANDLE* pointer = a;
- *pointer = reinterpret_cast<HANDLE>(value);
- }
-
- EXPECT_EQ(handle.Get(), reinterpret_cast<HANDLE>(value));
- HANDLE to_discard = handle.Take();
-
- // The standard use case:
- value = 183;
- CreateHandle(value, handle.Receive());
- EXPECT_EQ(handle.Get(), reinterpret_cast<HANDLE>(value));
- to_discard = handle.Take();
-}
diff --git a/chromium/base/win/scoped_process_information.cc b/chromium/base/win/scoped_process_information.cc
index cb7a30e2f54..bb2463774a8 100644
--- a/chromium/base/win/scoped_process_information.cc
+++ b/chromium/base/win/scoped_process_information.cc
@@ -15,7 +15,7 @@ namespace {
// Duplicates source into target, returning true upon success. |target| is
// guaranteed to be untouched in case of failure. Succeeds with no side-effects
// if source is NULL.
-bool CheckAndDuplicateHandle(HANDLE source, HANDLE* target) {
+bool CheckAndDuplicateHandle(HANDLE source, ScopedHandle* target) {
if (!source)
return true;
@@ -26,7 +26,7 @@ bool CheckAndDuplicateHandle(HANDLE source, HANDLE* target) {
DPLOG(ERROR) << "Failed to duplicate a handle.";
return false;
}
- *target = temp;
+ target->Set(temp);
return true;
}
@@ -36,13 +36,13 @@ ScopedProcessInformation::ScopedProcessInformation()
: process_id_(0), thread_id_(0) {
}
-ScopedProcessInformation::~ScopedProcessInformation() {
- Close();
+ScopedProcessInformation::ScopedProcessInformation(
+ const PROCESS_INFORMATION& process_info) : process_id_(0), thread_id_(0) {
+ Set(process_info);
}
-ScopedProcessInformation::Receiver ScopedProcessInformation::Receive() {
- DCHECK(!IsValid()) << "process_information_ must be NULL";
- return Receiver(this);
+ScopedProcessInformation::~ScopedProcessInformation() {
+ Close();
}
bool ScopedProcessInformation::IsValid() const {
@@ -72,10 +72,8 @@ bool ScopedProcessInformation::DuplicateFrom(
DCHECK(!IsValid()) << "target ScopedProcessInformation must be NULL";
DCHECK(other.IsValid()) << "source ScopedProcessInformation must be valid";
- if (CheckAndDuplicateHandle(other.process_handle(),
- process_handle_.Receive()) &&
- CheckAndDuplicateHandle(other.thread_handle(),
- thread_handle_.Receive())) {
+ if (CheckAndDuplicateHandle(other.process_handle(), &process_handle_) &&
+ CheckAndDuplicateHandle(other.thread_handle(), &thread_handle_)) {
process_id_ = other.process_id();
thread_id_ = other.thread_id();
return true;
diff --git a/chromium/base/win/scoped_process_information.h b/chromium/base/win/scoped_process_information.h
index 1f404c2dbac..2e240544122 100644
--- a/chromium/base/win/scoped_process_information.h
+++ b/chromium/base/win/scoped_process_information.h
@@ -18,33 +18,10 @@ namespace win {
// structures. Allows clients to take ownership of either handle independently.
class BASE_EXPORT ScopedProcessInformation {
public:
- // Helper object to contain the effect of Receive() to the funtion that needs
- // a pointer.
- class Receiver {
- public:
- explicit Receiver(ScopedProcessInformation* owner)
- : info_(),
- owner_(owner) {}
- ~Receiver() { owner_->Set(info_); }
-
- operator PROCESS_INFORMATION*() { return &info_; }
-
- private:
- PROCESS_INFORMATION info_;
- ScopedProcessInformation* owner_;
- };
-
ScopedProcessInformation();
+ explicit ScopedProcessInformation(const PROCESS_INFORMATION& process_info);
~ScopedProcessInformation();
- // Returns an object that may be passed to API calls such as CreateProcess.
- // DCHECKs that the object is not currently holding any handles.
- // HANDLEs stored in the returned PROCESS_INFORMATION will be owned by this
- // instance.
- // The intended use case is something like this:
- // if (::CreateProcess(..., startup_info, scoped_proces_info.Receive()))
- Receiver Receive();
-
// Returns true iff this instance is holding a thread and/or process handle.
bool IsValid() const;
diff --git a/chromium/base/win/scoped_process_information_unittest.cc b/chromium/base/win/scoped_process_information_unittest.cc
index b8ffc4427c9..550076ef9c6 100644
--- a/chromium/base/win/scoped_process_information_unittest.cc
+++ b/chromium/base/win/scoped_process_information_unittest.cc
@@ -19,11 +19,13 @@ const DWORD kThreadId = 1234;
const HANDLE kProcessHandle = reinterpret_cast<HANDLE>(7651);
const HANDLE kThreadHandle = reinterpret_cast<HANDLE>(1567);
-void MockCreateProcess(PROCESS_INFORMATION* process_info) {
- process_info->dwProcessId = kProcessId;
- process_info->dwThreadId = kThreadId;
- process_info->hProcess = kProcessHandle;
- process_info->hThread = kThreadHandle;
+void MockCreateProcess(base::win::ScopedProcessInformation* process_info) {
+ PROCESS_INFORMATION process_information = {};
+ process_information.dwProcessId = kProcessId;
+ process_information.dwThreadId = kThreadId;
+ process_information.hProcess = kProcessHandle;
+ process_information.hThread = kThreadHandle;
+ process_info->Set(process_information);
}
} // namespace
@@ -62,7 +64,7 @@ TEST_F(ScopedProcessInformationTest, InitiallyInvalid) {
TEST_F(ScopedProcessInformationTest, Receive) {
base::win::ScopedProcessInformation process_info;
- MockCreateProcess(process_info.Receive());
+ MockCreateProcess(&process_info);
EXPECT_TRUE(process_info.IsValid());
EXPECT_EQ(kProcessId, process_info.process_id());
@@ -74,7 +76,7 @@ TEST_F(ScopedProcessInformationTest, Receive) {
TEST_F(ScopedProcessInformationTest, TakeProcess) {
base::win::ScopedProcessInformation process_info;
- MockCreateProcess(process_info.Receive());
+ MockCreateProcess(&process_info);
HANDLE process = process_info.TakeProcessHandle();
EXPECT_EQ(kProcessHandle, process);
@@ -86,7 +88,7 @@ TEST_F(ScopedProcessInformationTest, TakeProcess) {
TEST_F(ScopedProcessInformationTest, TakeThread) {
base::win::ScopedProcessInformation process_info;
- MockCreateProcess(process_info.Receive());
+ MockCreateProcess(&process_info);
HANDLE thread = process_info.TakeThreadHandle();
EXPECT_EQ(kThreadHandle, thread);
@@ -98,7 +100,7 @@ TEST_F(ScopedProcessInformationTest, TakeThread) {
TEST_F(ScopedProcessInformationTest, TakeBoth) {
base::win::ScopedProcessInformation process_info;
- MockCreateProcess(process_info.Receive());
+ MockCreateProcess(&process_info);
HANDLE process = process_info.TakeProcessHandle();
HANDLE thread = process_info.TakeThreadHandle();
@@ -108,7 +110,7 @@ TEST_F(ScopedProcessInformationTest, TakeBoth) {
TEST_F(ScopedProcessInformationTest, TakeWholeStruct) {
base::win::ScopedProcessInformation process_info;
- MockCreateProcess(process_info.Receive());
+ MockCreateProcess(&process_info);
PROCESS_INFORMATION to_discard = process_info.Take();
EXPECT_EQ(kProcessId, to_discard.dwProcessId);
@@ -119,8 +121,11 @@ TEST_F(ScopedProcessInformationTest, TakeWholeStruct) {
}
TEST_F(ScopedProcessInformationTest, Duplicate) {
+ PROCESS_INFORMATION temp_process_information;
+ DoCreateProcess("ReturnSeven", &temp_process_information);
base::win::ScopedProcessInformation process_info;
- DoCreateProcess("ReturnSeven", process_info.Receive());
+ process_info.Set(temp_process_information);
+
base::win::ScopedProcessInformation duplicate;
duplicate.DuplicateFrom(process_info);
@@ -146,15 +151,17 @@ TEST_F(ScopedProcessInformationTest, Duplicate) {
}
TEST_F(ScopedProcessInformationTest, Set) {
- PROCESS_INFORMATION base_process_info = {};
+ base::win::ScopedProcessInformation base_process_info;
MockCreateProcess(&base_process_info);
+ PROCESS_INFORMATION base_struct = base_process_info.Take();
+
base::win::ScopedProcessInformation process_info;
- process_info.Set(base_process_info);
+ process_info.Set(base_struct);
EXPECT_EQ(kProcessId, process_info.process_id());
EXPECT_EQ(kThreadId, process_info.thread_id());
EXPECT_EQ(kProcessHandle, process_info.process_handle());
EXPECT_EQ(kThreadHandle, process_info.thread_handle());
- base_process_info = process_info.Take();
+ base_struct = process_info.Take();
}
diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h
index c68edb9c6fa..0f5fb0f686b 100644
--- a/chromium/base/win/shortcut.h
+++ b/chromium/base/win/shortcut.h
@@ -130,11 +130,13 @@ BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path,
string16* args);
// Pins a shortcut to the Windows 7 taskbar. The shortcut file must already
-// exist and be a shortcut that points to an executable.
+// exist and be a shortcut that points to an executable. The app id of the
+// shortcut is used to group windows and must be set correctly.
BASE_EXPORT bool TaskbarPinShortcutLink(const wchar_t* shortcut);
// Unpins a shortcut from the Windows 7 taskbar. The shortcut must exist and
-// already be pinned to the taskbar.
+// already be pinned to the taskbar. The app id of the shortcut is used as the
+// identifier for the taskbar item to remove and must be set correctly.
BASE_EXPORT bool TaskbarUnpinShortcutLink(const wchar_t* shortcut);
} // namespace win
diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc
index b3247b6184f..eaf152eb9dc 100644
--- a/chromium/base/win/shortcut_unittest.cc
+++ b/chromium/base/win/shortcut_unittest.cc
@@ -52,7 +52,7 @@ class ShortcutTest : public testing::Test {
arraysize(kFileContents2));
FilePath icon_path_2;
- file_util::CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2);
+ base::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());
@@ -91,7 +91,7 @@ TEST_F(ShortcutTest, CreateAndResolveShortcut) {
EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, NULL));
char read_contents[arraysize(kFileContents)];
- file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
EXPECT_STREQ(kFileContents, read_contents);
}
@@ -104,7 +104,7 @@ TEST_F(ShortcutTest, ResolveShortcutWithArgs) {
EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, &args));
char read_contents[arraysize(kFileContents)];
- file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
EXPECT_STREQ(kFileContents, read_contents);
EXPECT_EQ(link_properties_.arguments, args);
}
@@ -157,7 +157,7 @@ TEST_F(ShortcutTest, UpdateShortcutUpdateOnlyTargetAndResolve) {
EXPECT_TRUE(ResolveShortcut(link_file_, &resolved_name, NULL));
char read_contents[arraysize(kFileContents2)];
- file_util::ReadFile(resolved_name, read_contents, arraysize(read_contents));
+ base::ReadFile(resolved_name, read_contents, arraysize(read_contents));
EXPECT_STREQ(kFileContents2, read_contents);
}
diff --git a/chromium/base/win/startup_information_unittest.cc b/chromium/base/win/startup_information_unittest.cc
index 1903564d87b..d637ebd68a1 100644
--- a/chromium/base/win/startup_information_unittest.cc
+++ b/chromium/base/win/startup_information_unittest.cc
@@ -38,7 +38,6 @@ TEST_F(StartupInformationTest, InheritStdOut) {
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return;
- base::win::ScopedProcessInformation process_info;
base::win::StartupInformation startup_info;
HANDLE section = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
@@ -65,10 +64,13 @@ TEST_F(StartupInformationTest, InheritStdOut) {
std::wstring cmd_line =
this->MakeCmdLine("FireInheritedEvents", false).GetCommandLineString();
+ PROCESS_INFORMATION temp_process_info = {};
ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(cmd_line.c_str()),
NULL, NULL, true, EXTENDED_STARTUPINFO_PRESENT,
NULL, NULL, startup_info.startup_info(),
- process_info.Receive())) << ::GetLastError();
+ &temp_process_info)) << ::GetLastError();
+ base::win::ScopedProcessInformation process_info(temp_process_info);
+
// Only the first event should be signalled
EXPECT_EQ(WAIT_OBJECT_0, ::WaitForMultipleObjects(2, events, false,
4000));
diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc
index 813af316c00..7b85418a1ea 100644
--- a/chromium/base/win/win_util.cc
+++ b/chromium/base/win/win_util.cc
@@ -5,6 +5,7 @@
#include "base/win/win_util.h"
#include <aclapi.h>
+#include <lm.h>
#include <shellapi.h>
#include <shlobj.h>
#include <shobjidl.h> // Must be before propkey.
@@ -328,7 +329,7 @@ bool DismissVirtualKeyboard() {
typedef HWND (*MetroRootWindow) ();
-// As for this writing, GetMonitorInfo function seem to return wrong values
+// 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.
@@ -341,6 +342,11 @@ BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) {
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"));
}
@@ -350,6 +356,16 @@ BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) {
return ret;
}
+bool IsEnrolledToDomain() {
+ LPWSTR domain;
+ NETSETUP_JOIN_STATUS join_status;
+ if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success)
+ return false;
+ ::NetApiBufferFree(domain);
+
+ return join_status == ::NetSetupDomainName;
+}
+
} // namespace win
} // namespace base
diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h
index 903fcf971b8..eebb64ac8ef 100644
--- a/chromium/base/win/win_util.h
+++ b/chromium/base/win/win_util.h
@@ -130,6 +130,9 @@ BASE_EXPORT bool DismissVirtualKeyboard();
// 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();
+
} // namespace win
} // namespace base
diff --git a/chromium/base/win/windows_version.cc b/chromium/base/win/windows_version.cc
index 817a2bceb43..a99cd1840ac 100644
--- a/chromium/base/win/windows_version.cc
+++ b/chromium/base/win/windows_version.cc
@@ -10,6 +10,10 @@
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
+namespace {
+typedef BOOL (WINAPI *GetProductInfoPtr)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+}
+
namespace base {
namespace win {
@@ -34,7 +38,7 @@ OSInfo::OSInfo()
architecture_(OTHER_ARCHITECTURE),
wow64_status_(GetWOW64StatusForProcess(GetCurrentProcess())) {
OSVERSIONINFOEX version_info = { sizeof version_info };
- GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
+ ::GetVersionEx(reinterpret_cast<OSVERSIONINFO*>(&version_info));
version_number_.major = version_info.dwMajorVersion;
version_number_.minor = version_info.dwMinorVersion;
version_number_.build = version_info.dwBuildNumber;
@@ -68,7 +72,7 @@ OSInfo::OSInfo()
service_pack_.minor = version_info.wServicePackMinor;
SYSTEM_INFO system_info = { 0 };
- GetNativeSystemInfo(&system_info);
+ ::GetNativeSystemInfo(&system_info);
switch (system_info.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_INTEL: architecture_ = X86_ARCHITECTURE; break;
case PROCESSOR_ARCHITECTURE_AMD64: architecture_ = X64_ARCHITECTURE; break;
@@ -76,6 +80,64 @@ OSInfo::OSInfo()
}
processors_ = system_info.dwNumberOfProcessors;
allocation_granularity_ = system_info.dwAllocationGranularity;
+
+ GetProductInfoPtr get_product_info;
+ DWORD os_type;
+
+ if (version_info.dwMajorVersion == 6) {
+ // Only present on Vista+.
+ get_product_info = reinterpret_cast<GetProductInfoPtr>(
+ ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "GetProductInfo"));
+
+ get_product_info(version_info.dwMajorVersion, version_info.dwMinorVersion,
+ 0, 0, &os_type);
+ switch (os_type) {
+ case PRODUCT_CLUSTER_SERVER:
+ case PRODUCT_DATACENTER_SERVER:
+ case PRODUCT_DATACENTER_SERVER_CORE:
+ case PRODUCT_ENTERPRISE_SERVER:
+ case PRODUCT_ENTERPRISE_SERVER_CORE:
+ case PRODUCT_ENTERPRISE_SERVER_IA64:
+ case PRODUCT_SMALLBUSINESS_SERVER:
+ case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
+ case PRODUCT_STANDARD_SERVER:
+ case PRODUCT_STANDARD_SERVER_CORE:
+ case PRODUCT_WEB_SERVER:
+ version_type_ = SUITE_SERVER;
+ break;
+ case PRODUCT_PROFESSIONAL:
+ case PRODUCT_ULTIMATE:
+ case PRODUCT_ENTERPRISE:
+ case PRODUCT_BUSINESS:
+ version_type_ = SUITE_PROFESSIONAL;
+ break;
+ case PRODUCT_HOME_BASIC:
+ case PRODUCT_HOME_PREMIUM:
+ case PRODUCT_STARTER:
+ default:
+ version_type_ = SUITE_HOME;
+ break;
+ }
+ } else if (version_info.dwMajorVersion == 5 &&
+ version_info.dwMinorVersion == 2) {
+ if (version_info.wProductType == VER_NT_WORKSTATION &&
+ system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
+ version_type_ = SUITE_PROFESSIONAL;
+ } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER ) {
+ version_type_ = SUITE_HOME;
+ } else {
+ version_type_ = SUITE_SERVER;
+ }
+ } else if (version_info.dwMajorVersion == 5 &&
+ version_info.dwMinorVersion == 1) {
+ if(version_info.wSuiteMask & VER_SUITE_PERSONAL)
+ version_type_ = SUITE_HOME;
+ else
+ version_type_ = SUITE_PROFESSIONAL;
+ } else {
+ // Windows is pre XP so we don't care but pick a safe default.
+ version_type_ = SUITE_HOME;
+ }
}
OSInfo::~OSInfo() {
diff --git a/chromium/base/win/windows_version.h b/chromium/base/win/windows_version.h
index 718ba35e087..e51840b0db8 100644
--- a/chromium/base/win/windows_version.h
+++ b/chromium/base/win/windows_version.h
@@ -30,6 +30,16 @@ enum Version {
VERSION_WIN_LAST, // Indicates error condition.
};
+// A rough bucketing of the available types of versions of Windows. This is used
+// to distinguish enterprise enabled versions from home versions and potentially
+// server versions.
+enum VersionType {
+ SUITE_HOME,
+ SUITE_PROFESSIONAL,
+ SUITE_SERVER,
+ SUITE_LAST,
+};
+
// A singleton that can be used to query various pieces of information about the
// OS and process state. Note that this doesn't use the base Singleton class, so
// it can be used without an AtExitManager.
@@ -74,6 +84,7 @@ class BASE_EXPORT OSInfo {
Version version() const { return version_; }
// The next two functions return arrays of values, [major, minor(, build)].
VersionNumber version_number() const { return version_number_; }
+ VersionType version_type() const { return version_type_; }
ServicePack service_pack() const { return service_pack_; }
WindowsArchitecture architecture() const { return architecture_; }
int processors() const { return processors_; }
@@ -91,6 +102,7 @@ class BASE_EXPORT OSInfo {
Version version_;
VersionNumber version_number_;
+ VersionType version_type_;
ServicePack service_pack_;
WindowsArchitecture architecture_;
int processors_;
diff --git a/chromium/base/x11/edid_parser_x11.cc b/chromium/base/x11/edid_parser_x11.cc
new file mode 100644
index 00000000000..74b14b65c43
--- /dev/null
+++ b/chromium/base/x11/edid_parser_x11.cc
@@ -0,0 +1,196 @@
+// 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
new file mode 100644
index 00000000000..0fba0b54d7b
--- /dev/null
+++ b/chromium/base/x11/edid_parser_x11.h
@@ -0,0 +1,54 @@
+// 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
new file mode 100644
index 00000000000..97e3ce10340
--- /dev/null
+++ b/chromium/base/x11/edid_parser_x11_unittest.cc
@@ -0,0 +1,167 @@
+// 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
new file mode 100644
index 00000000000..446408c5c3d
--- /dev/null
+++ b/chromium/base/x11/x11_error_tracker.cc
@@ -0,0 +1,37 @@
+// 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
new file mode 100644
index 00000000000..5542f52a056
--- /dev/null
+++ b/chromium/base/x11/x11_error_tracker.h
@@ -0,0 +1,38 @@
+// 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
new file mode 100644
index 00000000000..7a78a7c2750
--- /dev/null
+++ b/chromium/base/x11/x11_error_tracker_gtk.cc
@@ -0,0 +1,29 @@
+// 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