summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 21:49:38 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 21:49:38 +0000
commitd7cae12696b96500c05dd2d430f6238922c20c96 (patch)
treeecff27b367735535b2a66477f8cd89d3c462a6c0 /base
parentee2815e28d408216cf94e874825b6bcf76c69083 (diff)
downloadchromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.zip
chromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.tar.gz
chromium_src-d7cae12696b96500c05dd2d430f6238922c20c96.tar.bz2
Add base to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/SConscript296
-rw-r--r--base/SConstruct3
-rw-r--r--base/atomic.h111
-rw-r--r--base/atomic_unittest.cc63
-rw-r--r--base/base.sln109
-rw-r--r--base/base.xcodeproj/project.pbxproj970
-rw-r--r--base/base_drag_source.cc88
-rw-r--r--base/base_drag_source.h71
-rw-r--r--base/base_drop_target.cc187
-rw-r--r--base/base_drop_target.h142
-rw-r--r--base/base_paths.cc151
-rw-r--r--base/base_paths.h70
-rw-r--r--base/base_switches.cc58
-rw-r--r--base/base_switches.h47
-rw-r--r--base/basictypes.h403
-rw-r--r--base/build/base.vcproj719
-rw-r--r--base/build/base.vsprops8
-rw-r--r--base/build/base_gfx.vcproj263
-rw-r--r--base/build/base_gfx.vsprops8
-rw-r--r--base/build/base_unittests.vcproj370
-rw-r--r--base/build/base_unittests.vsprops8
-rw-r--r--base/build/debug_message.vcproj151
-rw-r--r--base/build/singleton_dll_unittest.def9
-rw-r--r--base/build/singleton_dll_unittest.vsprops15
-rw-r--r--base/build/singleton_unittest.vcproj157
-rw-r--r--base/check_handler.h112
-rw-r--r--base/check_handler_unittest.cc72
-rw-r--r--base/clipboard.cc658
-rw-r--r--base/clipboard.h126
-rw-r--r--base/clipboard_unittest.cc211
-rw-r--r--base/clipboard_util.cc400
-rw-r--r--base/clipboard_util.h76
-rw-r--r--base/command_line.cc246
-rw-r--r--base/command_line.h119
-rw-r--r--base/command_line_unittest.cc125
-rw-r--r--base/condition_variable.cc464
-rw-r--r--base/condition_variable.h195
-rw-r--r--base/condition_variable_test.cc707
-rw-r--r--base/data/file_util_unittest/binary_file.binbin0 -> 538 bytes
-rw-r--r--base/data/file_util_unittest/binary_file_diff.binbin0 -> 538 bytes
-rw-r--r--base/data/file_util_unittest/binary_file_same.binbin0 -> 538 bytes
-rw-r--r--base/data/file_util_unittest/different.txt1
-rw-r--r--base/data/file_util_unittest/different_first.txt1
-rw-r--r--base/data/file_util_unittest/different_last.txt1
-rw-r--r--base/data/file_util_unittest/empty1.txt0
-rw-r--r--base/data/file_util_unittest/empty2.txt0
-rw-r--r--base/data/file_util_unittest/original.txt1
-rw-r--r--base/data/file_util_unittest/same.txt1
-rw-r--r--base/data/file_util_unittest/same_length.txt1
-rw-r--r--base/data/file_util_unittest/shortened.txt1
-rw-r--r--base/data/file_version_info_unittest/FileVersionInfoTest1.dllbin0 -> 13824 bytes
-rw-r--r--base/data/file_version_info_unittest/FileVersionInfoTest2.dllbin0 -> 13824 bytes
-rw-r--r--base/data/purify/base_unittests.exe.gtest.txt19
-rw-r--r--base/data/purify/base_unittests.exe_MLK.txt97
-rw-r--r--base/data/purify/base_unittests.exe_MLK_flakey.txt8
-rw-r--r--base/data/purify/base_unittests.exe_MLK_ignore.txt93
-rw-r--r--base/data/purify/base_unittests.exe_PAR_ignore.txt8
-rw-r--r--base/data/purify/base_unittests.exe_UMR.txt0
-rw-r--r--base/data/vectorcanvastest/basicdrawing/00_pc_clean.pngbin0 -> 289 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/00_vc_clean.pngbin0 -> 289 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/00_pc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/00_vc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/01_pc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/01_vc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/bitmap_alpha.pngbin0 -> 422 bytes
-rw-r--r--base/data/vectorcanvastest/bitmaps/bitmap_opaque.pngbin0 -> 3287 bytes
-rw-r--r--base/data/vectorcanvastest/circles/00_pc_circle_stroke.pngbin0 -> 383 bytes
-rw-r--r--base/data/vectorcanvastest/circles/00_vc_circle_stroke.pngbin0 -> 392 bytes
-rw-r--r--base/data/vectorcanvastest/circles/01_pc_circle_fill.pngbin0 -> 466 bytes
-rw-r--r--base/data/vectorcanvastest/circles/01_vc_circle_fill.pngbin0 -> 460 bytes
-rw-r--r--base/data/vectorcanvastest/circles/02_pc_circle_over_strike.pngbin0 -> 477 bytes
-rw-r--r--base/data/vectorcanvastest/circles/02_vc_circle_over_strike.pngbin0 -> 492 bytes
-rw-r--r--base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.pngbin0 -> 568 bytes
-rw-r--r--base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.pngbin0 -> 579 bytes
-rw-r--r--base/data/vectorcanvastest/circles/04_pc_mixed_stroke.pngbin0 -> 1172 bytes
-rw-r--r--base/data/vectorcanvastest/circles/04_vc_mixed_stroke.pngbin0 -> 1215 bytes
-rw-r--r--base/data/vectorcanvastest/clippingclean/00_pc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--base/data/vectorcanvastest/clippingclean/00_vc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--base/data/vectorcanvastest/clippingclean/01_pc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--base/data/vectorcanvastest/clippingclean/01_vc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--base/data/vectorcanvastest/clippingcombined/00_pc_combined.pngbin0 -> 1354 bytes
-rw-r--r--base/data/vectorcanvastest/clippingcombined/00_vc_combined.pngbin0 -> 1354 bytes
-rw-r--r--base/data/vectorcanvastest/clippingintersect/00_pc_intersect.pngbin0 -> 1214 bytes
-rw-r--r--base/data/vectorcanvastest/clippingintersect/00_vc_intersect.pngbin0 -> 1214 bytes
-rw-r--r--base/data/vectorcanvastest/clippingpath/00_pc_path.pngbin0 -> 1110 bytes
-rw-r--r--base/data/vectorcanvastest/clippingpath/00_vc_path.pngbin0 -> 1110 bytes
-rw-r--r--base/data/vectorcanvastest/clippingrect/00_pc_rect.pngbin0 -> 1459 bytes
-rw-r--r--base/data/vectorcanvastest/clippingrect/00_vc_rect.pngbin0 -> 1459 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/00_pc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/00_vc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.pngbin0 -> 735 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.pngbin0 -> 737 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.pngbin0 -> 756 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.pngbin0 -> 760 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/03_pc_se-nw.pngbin0 -> 765 bytes
-rw-r--r--base/data/vectorcanvastest/diagonallines/03_vc_se-nw.pngbin0 -> 781 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/00_pc_horizontal.pngbin0 -> 313 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/00_vc_horizontal.pngbin0 -> 319 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/01_pc_vertical.pngbin0 -> 328 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/01_vc_vertical.pngbin0 -> 344 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.pngbin0 -> 333 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.pngbin0 -> 348 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.pngbin0 -> 332 bytes
-rw-r--r--base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.pngbin0 -> 351 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/00_pc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/00_vc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/01_pc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/01_vc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/02_pc_scale.pngbin0 -> 6566 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/02_vc_scale.pngbin0 -> 12292 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/03_pc_rotate.pngbin0 -> 9749 bytes
-rw-r--r--base/data/vectorcanvastest/matrix/03_vc_rotate.pngbin0 -> 13795 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/00_pc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/00_vc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/01_pc_dash_path.pngbin0 -> 348 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/01_vc_dash_path.pngbin0 -> 343 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/02_pc_dash_rect.pngbin0 -> 387 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/02_vc_dash_rect.pngbin0 -> 395 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/03_pc_circle.pngbin0 -> 488 bytes
-rw-r--r--base/data/vectorcanvastest/patheffects/03_vc_circle.pngbin0 -> 517 bytes
-rw-r--r--base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.pngbin0 -> 313 bytes
-rw-r--r--base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.pngbin0 -> 319 bytes
-rw-r--r--base/data/vectorcanvastest/uninitialized/00_pc_empty.pngbin0 -> 290 bytes
-rw-r--r--base/data/vectorcanvastest/uninitialized/00_vc_empty.pngbin0 -> 109 bytes
-rw-r--r--base/debug_message.cc42
-rw-r--r--base/debug_on_start.cc89
-rw-r--r--base/debug_on_start.h75
-rw-r--r--base/debug_util.cc132
-rw-r--r--base/debug_util.h44
-rw-r--r--base/event_recorder.cc281
-rw-r--r--base/event_recorder.h115
-rw-r--r--base/file_util.cc852
-rw-r--r--base/file_util.h302
-rw-r--r--base/file_util_unittest.cc777
-rw-r--r--base/file_version_info.cc205
-rw-r--r--base/file_version_info.h97
-rw-r--r--base/file_version_info_unittest.cc153
-rw-r--r--base/fix_wp64.h100
-rw-r--r--base/fixed_string.h96
-rw-r--r--base/fixed_string_unittest.cc62
-rw-r--r--base/gfx/SConscript83
-rw-r--r--base/gfx/bitmap_header.cc88
-rw-r--r--base/gfx/bitmap_header.h56
-rw-r--r--base/gfx/bitmap_platform_device.cc482
-rw-r--r--base/gfx/bitmap_platform_device.h135
-rw-r--r--base/gfx/convolver.cc359
-rw-r--r--base/gfx/convolver.h156
-rw-r--r--base/gfx/convolver_unittest.cc151
-rw-r--r--base/gfx/font_utils.cc305
-rw-r--r--base/gfx/font_utils.h105
-rw-r--r--base/gfx/image_operations.cc387
-rw-r--r--base/gfx/image_operations.h87
-rw-r--r--base/gfx/image_operations_unittest.cc172
-rw-r--r--base/gfx/img_resize_perftest.cc94
-rw-r--r--base/gfx/native_theme.cc626
-rw-r--r--base/gfx/native_theme.h310
-rw-r--r--base/gfx/native_theme_unittest.cc36
-rw-r--r--base/gfx/platform_canvas.cc105
-rw-r--r--base/gfx/platform_canvas.h209
-rw-r--r--base/gfx/platform_canvas_unittest.cc293
-rw-r--r--base/gfx/platform_device.cc253
-rw-r--r--base/gfx/platform_device.h120
-rw-r--r--base/gfx/png_codec_unittest.cc223
-rw-r--r--base/gfx/png_decoder.cc376
-rw-r--r--base/gfx/png_decoder.h88
-rw-r--r--base/gfx/png_encoder.cc217
-rw-r--r--base/gfx/png_encoder.h83
-rw-r--r--base/gfx/point.cc52
-rw-r--r--base/gfx/point.h88
-rw-r--r--base/gfx/rect.cc217
-rw-r--r--base/gfx/rect.h153
-rw-r--r--base/gfx/rect_unittest.cc295
-rw-r--r--base/gfx/size.cc49
-rw-r--r--base/gfx/size.h91
-rw-r--r--base/gfx/skia_utils.cc99
-rw-r--r--base/gfx/skia_utils.h80
-rw-r--r--base/gfx/uniscribe.cc872
-rw-r--r--base/gfx/uniscribe.h390
-rw-r--r--base/gfx/uniscribe_unittest.cc164
-rw-r--r--base/gfx/vector_canvas.cc109
-rw-r--r--base/gfx/vector_canvas.h70
-rw-r--r--base/gfx/vector_canvas_unittest.cc1032
-rw-r--r--base/gfx/vector_device.cc646
-rw-r--r--base/gfx/vector_device.h146
-rw-r--r--base/hash_tables.h54
-rw-r--r--base/histogram.cc668
-rw-r--r--base/histogram.h469
-rw-r--r--base/histogram_test.cc319
-rw-r--r--base/hmac.cc121
-rw-r--r--base/hmac.h87
-rw-r--r--base/hmac_unittest.cc94
-rw-r--r--base/iat_patch.cc246
-rw-r--r--base/iat_patch.h140
-rw-r--r--base/icu_util.cc70
-rw-r--r--base/icu_util.h41
-rw-r--r--base/id_map.h122
-rw-r--r--base/idle_timer.cc103
-rw-r--r--base/idle_timer.h114
-rw-r--r--base/idletimer_unittest.cc221
-rw-r--r--base/image_util.cc97
-rw-r--r--base/image_util.h88
-rw-r--r--base/json_reader.cc605
-rw-r--r--base/json_reader.h165
-rw-r--r--base/json_reader_unittest.cc412
-rw-r--r--base/json_writer.cc191
-rw-r--r--base/json_writer.h71
-rw-r--r--base/json_writer_unittest.cc82
-rw-r--r--base/linked_ptr.h200
-rw-r--r--base/linked_ptr_unittest.cc136
-rw-r--r--base/lock.cc156
-rw-r--r--base/lock.h122
-rw-r--r--base/lock_impl.h87
-rw-r--r--base/lock_impl_mac.cc49
-rw-r--r--base/lock_impl_win.cc50
-rw-r--r--base/logging.cc396
-rw-r--r--base/logging.h523
-rw-r--r--base/md5.cc279
-rw-r--r--base/md5.h82
-rw-r--r--base/memory_debug.cc79
-rw-r--r--base/memory_debug.h66
-rw-r--r--base/message_loop.cc969
-rw-r--r--base/message_loop.h611
-rw-r--r--base/message_loop_unittest.cc827
-rw-r--r--base/multiprocess_test.h83
-rw-r--r--base/no_windows2000_unittest.h46
-rw-r--r--base/non_thread_safe.cc48
-rw-r--r--base/non_thread_safe.h79
-rw-r--r--base/observer_list.h170
-rw-r--r--base/observer_list_unittest.cc88
-rw-r--r--base/path_service.cc202
-rw-r--r--base/path_service.h85
-rw-r--r--base/path_service_unittest.cc57
-rw-r--r--base/pe_image.cc560
-rw-r--r--base/pe_image.h282
-rw-r--r--base/pe_image_unittest.cc226
-rw-r--r--base/perftimer.cc69
-rw-r--r--base/perftimer.h105
-rw-r--r--base/pickle.cc348
-rw-r--r--base/pickle.h259
-rw-r--r--base/pickle_unittest.cc238
-rw-r--r--base/platform_thread.cc51
-rw-r--r--base/platform_thread.h53
-rw-r--r--base/port.h57
-rw-r--r--base/pr_time_test.cc179
-rw-r--r--base/process.cc134
-rw-r--r--base/process.h106
-rw-r--r--base/process_util.cc581
-rw-r--r--base/process_util.h285
-rw-r--r--base/process_util_unittest.cc99
-rw-r--r--base/ref_counted.h249
-rw-r--r--base/ref_counted_unittest.cc41
-rw-r--r--base/registry.cc481
-rw-r--r--base/registry.h249
-rw-r--r--base/resource_util.cc57
-rw-r--r--base/resource_util.h49
-rw-r--r--base/revocable_store.cc72
-rw-r--r--base/revocable_store.h101
-rw-r--r--base/run_all_perftests.cc54
-rw-r--r--base/run_all_unittests.cc34
-rw-r--r--base/scoped_cftyperef.h97
-rw-r--r--base/scoped_handle.h213
-rw-r--r--base/scoped_ptr.h402
-rw-r--r--base/sha2.cc47
-rw-r--r--base/sha2.h52
-rw-r--r--base/sha2_unittest.cc103
-rw-r--r--base/shared_event.cc104
-rw-r--r--base/shared_event.h87
-rw-r--r--base/shared_event_unittest.cc81
-rw-r--r--base/shared_memory.cc187
-rw-r--r--base/shared_memory.h169
-rw-r--r--base/shared_memory_unittest.cc180
-rw-r--r--base/singleton.h293
-rw-r--r--base/singleton_dll_unittest.cc114
-rw-r--r--base/singleton_dll_unittest.h83
-rw-r--r--base/singleton_internal.h66
-rw-r--r--base/singleton_unittest.cc227
-rw-r--r--base/spin_wait.h74
-rw-r--r--base/stack_container.h255
-rw-r--r--base/stack_container_unittest.cc137
-rw-r--r--base/stats_counters.h297
-rw-r--r--base/stats_table.cc545
-rw-r--r--base/stats_table.h209
-rw-r--r--base/stats_table_unittest.cc396
-rw-r--r--base/string16.h217
-rw-r--r--base/string_escape.cc122
-rw-r--r--base/string_escape.h60
-rw-r--r--base/string_escape_unittest.cc79
-rw-r--r--base/string_piece.cc240
-rw-r--r--base/string_piece.h209
-rw-r--r--base/string_piece_unittest.cc565
-rw-r--r--base/string_tokenizer.h225
-rw-r--r--base/string_tokenizer_unittest.cc232
-rw-r--r--base/string_util.cc1021
-rw-r--r--base/string_util.h504
-rw-r--r--base/string_util_icu.cc201
-rw-r--r--base/string_util_mac.cc239
-rw-r--r--base/string_util_mac.h62
-rw-r--r--base/string_util_unittest.cc848
-rw-r--r--base/string_util_win.cc124
-rw-r--r--base/string_util_win.h76
-rw-r--r--base/task.h725
-rw-r--r--base/test_suite.h115
-rw-r--r--base/third_party/nspr/README.google2
-rw-r--r--base/third_party/nspr/prcpucfg.h41
-rw-r--r--base/third_party/nspr/prcpucfg_mac.h145
-rw-r--r--base/third_party/nspr/prcpucfg_win.h300
-rw-r--r--base/third_party/nspr/prtime.cc838
-rw-r--r--base/third_party/nspr/prtime.h185
-rw-r--r--base/third_party/nspr/prtypes.h567
-rw-r--r--base/third_party/nss/README.google8
-rw-r--r--base/third_party/nss/blapi.h101
-rw-r--r--base/third_party/nss/blapit.h91
-rw-r--r--base/third_party/nss/sha256.h51
-rw-r--r--base/third_party/nss/sha512.cc1396
-rw-r--r--base/third_party/purify/pure.h145
-rw-r--r--base/third_party/purify/pure_api.c145
-rw-r--r--base/thread.cc258
-rw-r--r--base/thread.h135
-rw-r--r--base/thread_local_storage.h95
-rw-r--r--base/thread_local_storage_unittest.cc109
-rw-r--r--base/thread_local_storage_win.cc177
-rw-r--r--base/thread_unittest.cc157
-rw-r--r--base/time.cc190
-rw-r--r--base/time.h469
-rw-r--r--base/time_mac.cc139
-rw-r--r--base/time_unittest.cc195
-rw-r--r--base/time_win.cc244
-rw-r--r--base/timer.cc346
-rw-r--r--base/timer.h320
-rw-r--r--base/timer_unittest.cc332
-rw-r--r--base/tracked.cc113
-rw-r--r--base/tracked.h129
-rw-r--r--base/tracked_objects.cc918
-rw-r--r--base/tracked_objects.h509
-rw-r--r--base/tracked_objects_test.cc122
-rw-r--r--base/tuple.h687
-rw-r--r--base/values.cc583
-rw-r--r--base/values.h382
-rw-r--r--base/values_unittest.cc403
-rw-r--r--base/watchdog.cc171
-rw-r--r--base/watchdog.h111
-rw-r--r--base/watchdog_test.cc153
-rw-r--r--base/win_util.cc359
-rw-r--r--base/win_util.h116
-rw-r--r--base/win_util_unittest.cc57
-rw-r--r--base/windows_message_list.h274
-rw-r--r--base/wmi_util.cc145
-rw-r--r--base/wmi_util.h98
-rw-r--r--base/wmi_util_unittest.cc80
-rw-r--r--base/word_iterator.cc88
-rw-r--r--base/word_iterator.h110
-rw-r--r--base/worker_pool.cc57
-rw-r--r--base/worker_pool.h50
371 files changed, 60061 insertions, 0 deletions
diff --git a/base/SConscript b/base/SConscript
new file mode 100644
index 0000000..2b7a7cc
--- /dev/null
+++ b/base/SConscript
@@ -0,0 +1,296 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+env_tests = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ '$ICU38_DIR/public/common',
+ '$ICU38_DIR/public/i18n',
+ '..',
+ ],
+ CPPDEFINES = [
+ 'U_STATIC_IMPLEMENTATION',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ 'base_drag_source.cc',
+ 'base_drop_target.cc',
+ 'base_paths.cc',
+ 'base_switches.cc',
+ 'clipboard.cc',
+ 'clipboard_util.cc',
+ 'command_line.cc',
+ 'condition_variable.cc',
+ 'debug_on_start.cc',
+ 'debug_util.cc',
+ 'event_recorder.cc',
+ 'file_util.cc',
+ 'file_version_info.cc',
+ 'histogram.cc',
+ 'hmac.cc',
+ 'iat_patch.cc',
+ 'icu_util.cc',
+ 'idle_timer.cc',
+ 'image_util.cc',
+ 'json_reader.cc',
+ 'json_writer.cc',
+ 'lock.cc',
+ 'lock_impl_win.cc',
+ 'logging.cc',
+ 'md5.cc',
+ 'memory_debug.cc',
+ 'message_loop.cc',
+ 'non_thread_safe.cc',
+ 'path_service.cc',
+ 'pe_image.cc',
+ 'pickle.cc',
+ 'platform_thread.cc',
+ 'process.cc',
+ 'process_util.cc',
+ 'registry.cc',
+ 'resource_util.cc',
+ 'revocable_store.cc',
+ 'sha2.cc',
+ 'shared_event.cc',
+ 'shared_memory.cc',
+ 'stats_table.cc',
+ 'string_escape.cc',
+ 'string_util.cc',
+ 'string_util_icu.cc',
+ 'string_util_win.cc',
+ 'third_party/nspr/prtime.cc',
+ 'third_party/nss/sha512.cc',
+ 'thread.cc',
+ 'thread_local_storage_win.cc',
+ 'time.cc',
+ 'time_win.cc',
+ 'timer.cc',
+ 'tracked.cc',
+ 'tracked_objects.cc',
+ 'values.cc',
+ 'watchdog.cc',
+ 'win_util.cc',
+ 'wmi_util.cc',
+ 'word_iterator.cc',
+ 'worker_pool.cc',
+]
+
+env.StaticLibrary('base', input_files)
+
+
+env_tests.Prepend(
+ CPPPATH = [
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/platform',
+ '$ZLIB_DIR',
+ '$LIBPNG_DIR',
+ '$ICU38_DIR/public/common',
+ '$ICU38/_DIRpublic/i18n',
+ '..',
+ ],
+ CPPDEFINES = [
+ 'UNIT_TEST',
+ 'PNG_USER_CONFIG',
+ 'CHROME_PNG_WRITE_SUPPORT',
+ 'U_STATIC_IMPLEMENTATION',
+ '_WIN32_WINNT=0x0600',
+ 'WINVER=0x0600',
+ '_HAS_EXCEPTIONS=0',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/WX',
+ '/Wp64',
+ '/TP',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+ LINKFLAGS = [
+ '/MANIFEST',
+ '/DELAYLOAD:"dwmapi.dll"',
+ '/DELAYLOAD:"uxtheme.dll"',
+ '/MACHINE:X86',
+ '/FIXED:No',
+
+ '/safeseh',
+ '/dynamicbase',
+ '/ignore:4199',
+ '/nxcompat',
+ ],
+ LIBS = [
+ 'advapi32.lib',
+ 'comdlg32.lib',
+ 'DelayImp.lib',
+ 'gdi32.lib',
+ 'kernel32.lib',
+ 'msimg32.lib',
+ 'odbc32.lib',
+ 'odbccp32.lib',
+ 'ole32.lib',
+ 'oleaut32.lib',
+ 'psapi.lib',
+ 'shell32.lib',
+ 'user32.lib',
+ 'usp10.lib',
+ 'uuid.lib',
+ 'version.lib',
+ 'wininet.lib',
+ 'winspool.lib',
+ 'ws2_32.lib',
+ ],
+)
+
+libs = [
+ 'base.lib',
+ 'gfx/base_gfx.lib',
+ '$SKIA_DIR/skia.lib',
+ '$LIBPNG_DIR/libpng.lib',
+ '$TESTING_DIR/gtest.lib',
+ '$ICU38_DIR/icuuc.lib',
+ '$ZLIB_DIR/zlib.lib',
+]
+
+env_tests.Append(
+ CPPPATH = [
+ '$GTEST_DIR/include',
+ ],
+)
+
+env_tests_dll = env_tests.Clone()
+env_tests_dll.Append(
+ CPPDEFINES = [
+ '_WINDLL',
+ 'SINGLETON_UNITTEST_EXPORTS',
+ ],
+)
+dll = env_tests_dll.SharedLibrary(['singleton_dll_unittest.dll',
+ 'singleton_dll_unittest.ilk',
+ 'singleton_dll_unittest.pdb'],
+ ['singleton_dll_unittest.cc',
+ 'build/singleton_dll_unittest.def'] + libs)
+i = env.Install('$TARGET_ROOT', dll[0])
+env.Alias('base', i)
+
+env_tests.Program(['debug_message.exe',
+ 'debug_message.ilk',
+ 'debug_message.pdb'],
+ ['debug_message.cc'] + libs)
+
+test_files = [
+ 'atomic_unittest.cc',
+ 'check_handler_unittest.cc',
+ 'clipboard_unittest.cc',
+ 'command_line_unittest.cc',
+ 'condition_variable_test.cc',
+ 'file_util_unittest.cc',
+ 'file_version_info_unittest.cc',
+ 'fixed_string_unittest.cc',
+ 'gfx/convolver_unittest.cc',
+ 'gfx/image_operations_unittest.cc',
+ 'gfx/native_theme_unittest.cc',
+ 'gfx/platform_canvas_unittest.cc',
+ 'gfx/png_codec_unittest.cc',
+ 'gfx/rect_unittest.cc',
+ 'gfx/uniscribe_unittest.cc',
+ 'gfx/vector_canvas_unittest.cc',
+ 'hmac_unittest.cc',
+ 'json_reader_unittest.cc',
+ 'json_writer_unittest.cc',
+ 'linked_ptr_unittest.cc',
+ 'message_loop_unittest.cc',
+ 'path_service_unittest.cc',
+ 'pe_image_unittest.cc',
+ 'pickle_unittest.cc',
+ 'pr_time_test.cc',
+ 'process_util_unittest.cc',
+ 'ref_counted_unittest.cc',
+ 'run_all_unittests.cc',
+ 'sha2_unittest.cc',
+ 'shared_event_unittest.cc',
+ 'shared_memory_unittest.cc',
+ 'singleton_unittest.cc',
+ 'stack_container_unittest.cc',
+ 'stats_table_unittest.cc',
+ 'string_tokenizer_unittest.cc',
+ 'string_util_unittest.cc',
+ 'thread_local_storage_unittest.cc',
+ 'thread_unittest.cc',
+ 'tracked_objects_test.cc',
+ 'time_unittest.cc',
+ 'timer_unittest.cc',
+ 'values_unittest.cc',
+ 'win_util_unittest.cc',
+ 'wmi_util_unittest.cc',
+
+ 'singleton_dll_unittest.lib',
+]
+
+base_unittests = env_tests.Program([
+ 'base_unittests',
+ 'base_unittests.exp',
+ 'base_unittests.ilk',
+ 'base_unittests.lib',
+ 'base_unittests.pdb'], test_files + libs)
+
+
+# Install up a level to allow unit test path assumptions to be valid.
+installed_base_unittests = env.Install('$TARGET_ROOT', base_unittests)
+
+
+sconscript_dirs = [
+ 'gfx/SConscript',
+]
+
+SConscript(sconscript_dirs, exports=['env'])
+
+
+# Setup alias for all base related targets.
+env.Alias('base', ['.', installed_base_unittests, '../icudt38.dll'])
+
+
+env_tests.StaticObject('perftimer.cc')
+env_tests.StaticObject('run_all_perftests.cc')
diff --git a/base/SConstruct b/base/SConstruct
new file mode 100644
index 0000000..e9acd54
--- /dev/null
+++ b/base/SConstruct
@@ -0,0 +1,3 @@
+build_component = 'base'
+SConscript('../build/SConscript.main',
+ exports=['build_component'])
diff --git a/base/atomic.h b/base/atomic.h
new file mode 100644
index 0000000..becbcc3
--- /dev/null
+++ b/base/atomic.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ATOMIC_H__
+#define BASE_ATOMIC_H__
+
+#if defined(WIN32)
+#include <windows.h>
+#elif defined(__APPLE__)
+#include <libkern/OSAtomic.h>
+#else
+#error Implement atomic support on your platform
+#endif
+
+#include "base/basictypes.h"
+
+namespace base {
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Forward declarations and descriptions of the functions
+//
+///////////////////////////////////////////////////////////////////////////
+
+// Atomically increments *value and returns the resulting incremented value.
+// This function implies no memory barriers.
+int32 AtomicIncrement(volatile int32* value);
+
+// Atomically decrements *value and returns the resulting decremented value.
+// This function implies no memory barriers.
+int32 AtomicDecrement(volatile int32* value);
+
+// Atomically sets *target to new_value and returns the old value of *target.
+// This function implies no memory barriers.
+int32 AtomicSwap(volatile int32* target, int32 new_value);
+
+///////////////////////////////////////////////////////////////////////////
+//
+// Implementations for various platforms
+//
+///////////////////////////////////////////////////////////////////////////
+
+#if defined(WIN32)
+
+inline int32 AtomicIncrement(volatile int32* value) {
+ return InterlockedIncrement(reinterpret_cast<volatile LONG*>(value));
+}
+
+inline int32 AtomicDecrement(volatile int32* value) {
+ return InterlockedDecrement(reinterpret_cast<volatile LONG*>(value));
+}
+
+inline int32 AtomicSwap(volatile int32* target, int32 new_value) {
+ return InterlockedExchange(reinterpret_cast<volatile LONG*>(target),
+ new_value);
+}
+
+#elif defined(__APPLE__)
+
+inline int32 AtomicIncrement(volatile int32* value) {
+ return OSAtomicIncrement32(reinterpret_cast<volatile int32_t*>(value));
+}
+
+inline int32 AtomicDecrement(volatile int32* value) {
+ return OSAtomicDecrement32(reinterpret_cast<volatile int32_t*>(value));
+}
+
+inline int32 AtomicSwap(volatile int32* target, int32 new_value) {
+ int32 old_value;
+ do {
+ old_value = *target;
+ } while (!OSAtomicCompareAndSwap32(old_value, new_value,
+ reinterpret_cast<volatile int32_t*>(target)));
+ return old_value;
+}
+
+#else
+
+#error Implement atomic support on your platform
+
+#endif
+
+} // namespace base
+
+#endif // BASE_ATOMIC_H__
diff --git a/base/atomic_unittest.cc b/base/atomic_unittest.cc
new file mode 100644
index 0000000..770a520
--- /dev/null
+++ b/base/atomic_unittest.cc
@@ -0,0 +1,63 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/atomic.h"
+
+#include "base/lock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(AtomicTest, Increment) {
+ int32 value = 38;
+ int32 new_value = base::AtomicIncrement(&value);
+ EXPECT_EQ(39, value);
+ EXPECT_EQ(39, new_value);
+}
+
+TEST(AtomicTest, Decrement) {
+ int32 value = 49;
+ int32 new_value = base::AtomicDecrement(&value);
+ EXPECT_EQ(48, value);
+ EXPECT_EQ(48, new_value);
+}
+
+TEST(AtomicTest, Swap) {
+ int32 value = 38;
+ int32 old_value = base::AtomicSwap(&value, 49);
+ EXPECT_EQ(49, value);
+ EXPECT_EQ(38, old_value);
+
+ // Now do another test.
+ value = 0;
+ old_value = base::AtomicSwap(&value, 1);
+ EXPECT_EQ(0, old_value);
+ old_value = base::AtomicSwap(&value, 1);
+ EXPECT_EQ(1, old_value);
+ old_value = base::AtomicSwap(&value, 1);
+ EXPECT_EQ(1, old_value);
+}
diff --git a/base/base.sln b/base/base.sln
new file mode 100644
index 0000000..c928064
--- /dev/null
+++ b/base/base.sln
@@ -0,0 +1,109 @@
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "build\base.vcproj", "{1832A374-8A74-4F9E-B536-69A699B3E165}"
+ ProjectSection(ProjectDependencies) = postProject
+ {0E5474AC-5996-4B13-87C0-4AE931EE0815} = {0E5474AC-5996-4B13-87C0-4AE931EE0815}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base_unittests", "build\base_unittests.vcproj", "{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C} = {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}
+ {8C27D792-2648-4F5E-9ED0-374276327308} = {8C27D792-2648-4F5E-9ED0-374276327308}
+ {A508ADD3-CECE-4E0F-8448-2F5E454DF551} = {A508ADD3-CECE-4E0F-8448-2F5E454DF551}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD} = {C564F145-9172-42C3-BFCB-6014CA97DBCD}
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D} = {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}
+ {E457F2FB-4708-4001-9B1C-275D7BD7F2A8} = {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "debug_message", "build\debug_message.vcproj", "{0E5474AC-5996-4B13-87C0-4AE931EE0815}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dependencies", "dependencies", "{F216062D-F9C4-4883-A52C-2BE9ECADEEA0}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "icuuc", "..\third_party\icu38\build\icuuc.vcproj", "{8C27D792-2648-4F5E-9ED0-374276327308}"
+ ProjectSection(ProjectDependencies) = postProject
+ {A0D94973-D355-47A5-A1E2-3456F321F010} = {A0D94973-D355-47A5-A1E2-3456F321F010}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "icudt", "..\third_party\icu38\build\icudt.vcproj", "{A0D94973-D355-47A5-A1E2-3456F321F010}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base_gfx", "build\base_gfx.vcproj", "{A508ADD3-CECE-4E0F-8448-2F5E454DF551}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "skia", "..\skia\skia.vcproj", "{CD9CA56E-4E94-444C-87D4-58CA1E6F300D}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "..\third_party\libpng\libpng.vcproj", "{C564F145-9172-42C3-BFCB-6014CA97DBCD}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\third_party\zlib\zlib.vcproj", "{8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "singleton_dll_unittest", "build\singleton_unittest.vcproj", "{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}"
+ ProjectSection(ProjectDependencies) = postProject
+ {1832A374-8A74-4F9E-B536-69A699B3E165} = {1832A374-8A74-4F9E-B536-69A699B3E165}
+ {8C27D792-2648-4F5E-9ED0-374276327308} = {8C27D792-2648-4F5E-9ED0-374276327308}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gtest", "..\testing\gtest.vcproj", "{BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0E5474AC-5996-4B13-87C0-4AE931EE0815}.Debug|Win32.ActiveCfg = Debug|Win32
+ {0E5474AC-5996-4B13-87C0-4AE931EE0815}.Debug|Win32.Build.0 = Debug|Win32
+ {0E5474AC-5996-4B13-87C0-4AE931EE0815}.Release|Win32.ActiveCfg = Release|Win32
+ {0E5474AC-5996-4B13-87C0-4AE931EE0815}.Release|Win32.Build.0 = Release|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.ActiveCfg = Debug|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Debug|Win32.Build.0 = Debug|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.ActiveCfg = Release|Win32
+ {1832A374-8A74-4F9E-B536-69A699B3E165}.Release|Win32.Build.0 = Release|Win32
+ {27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Debug|Win32.ActiveCfg = Debug|Win32
+ {27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Debug|Win32.Build.0 = Debug|Win32
+ {27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Release|Win32.ActiveCfg = Release|Win32
+ {27A30967-4BBA-48D1-8522-CDE95F7B1CEC}.Release|Win32.Build.0 = Release|Win32
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Debug|Win32.Build.0 = Debug|Win32
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Release|Win32.ActiveCfg = Release|Win32
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C}.Release|Win32.Build.0 = Release|Win32
+ {8C27D792-2648-4F5E-9ED0-374276327308}.Debug|Win32.ActiveCfg = Debug|Win32
+ {8C27D792-2648-4F5E-9ED0-374276327308}.Debug|Win32.Build.0 = Debug|Win32
+ {8C27D792-2648-4F5E-9ED0-374276327308}.Release|Win32.ActiveCfg = Release|Win32
+ {8C27D792-2648-4F5E-9ED0-374276327308}.Release|Win32.Build.0 = Release|Win32
+ {A0D94973-D355-47A5-A1E2-3456F321F010}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A0D94973-D355-47A5-A1E2-3456F321F010}.Debug|Win32.Build.0 = Debug|Win32
+ {A0D94973-D355-47A5-A1E2-3456F321F010}.Release|Win32.ActiveCfg = Release|Win32
+ {A0D94973-D355-47A5-A1E2-3456F321F010}.Release|Win32.Build.0 = Release|Win32
+ {A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Debug|Win32.Build.0 = Debug|Win32
+ {A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Release|Win32.ActiveCfg = Release|Win32
+ {A508ADD3-CECE-4E0F-8448-2F5E454DF551}.Release|Win32.Build.0 = Release|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.ActiveCfg = Debug|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Debug|Win32.Build.0 = Debug|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.ActiveCfg = Release|Win32
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B}.Release|Win32.Build.0 = Release|Win32
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD}.Debug|Win32.ActiveCfg = Debug|Win32
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD}.Debug|Win32.Build.0 = Debug|Win32
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD}.Release|Win32.ActiveCfg = Release|Win32
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD}.Release|Win32.Build.0 = Release|Win32
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Debug|Win32.Build.0 = Debug|Win32
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Release|Win32.ActiveCfg = Release|Win32
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D}.Release|Win32.Build.0 = Release|Win32
+ {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Debug|Win32.Build.0 = Debug|Win32
+ {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Release|Win32.ActiveCfg = Release|Win32
+ {E457F2FB-4708-4001-9B1C-275D7BD7F2A8}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {8423AF0D-4B88-4EBF-94E1-E4D00D00E21C} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ {8C27D792-2648-4F5E-9ED0-374276327308} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ {A0D94973-D355-47A5-A1E2-3456F321F010} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ {BFE8E2A7-3B3B-43B0-A994-3058B852DB8B} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ {C564F145-9172-42C3-BFCB-6014CA97DBCD} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ {CD9CA56E-4E94-444C-87D4-58CA1E6F300D} = {F216062D-F9C4-4883-A52C-2BE9ECADEEA0}
+ EndGlobalSection
+EndGlobal
diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..64eaff5
--- /dev/null
+++ b/base/base.xcodeproj/project.pbxproj
@@ -0,0 +1,970 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 42;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 825404020D92D3340006B936 /* All */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 825404110D92D35C0006B936 /* Build configuration list for PBXAggregateTarget "All" */;
+ buildPhases = (
+ );
+ dependencies = (
+ 825404060D92D33A0006B936 /* PBXTargetDependency */,
+ 825404080D92D33C0006B936 /* PBXTargetDependency */,
+ );
+ name = All;
+ productName = All;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 7B5AD60E0D9DD8050012BCF1 /* scoped_cftyperef.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */; };
+ 7BD9E84F0DA447F800FC7A01 /* singleton.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BD9E84E0DA447F800FC7A01 /* singleton.h */; };
+ 7BEB81110D9AD288009BA8DD /* prcpucfg_mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */; };
+ 7BEB814A0D9B0F33009BA8DD /* time_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB81490D9B0F33009BA8DD /* time_mac.cc */; };
+ 7BEB834E0D9C4BE0009BA8DD /* string_util_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */; };
+ 7BEE52C20D9D84FD0067FF23 /* string_util_mac.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */; };
+ 7BEFC29E0D99832D000829AD /* lock_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 7BEFC29C0D99832D000829AD /* lock_impl.h */; };
+ 7BEFC29F0D99832D000829AD /* lock_impl_mac.cc in Sources */ = {isa = PBXBuildFile; fileRef = 7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */; };
+ 821B91690DAABD7F00F350D7 /* string16.h in Headers */ = {isa = PBXBuildFile; fileRef = 821B91680DAABD7F00F350D7 /* string16.h */; };
+ 825402CE0D92D1390006B936 /* base_drag_source.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C50D92D1390006B936 /* base_drag_source.cc */; };
+ 825402CF0D92D1390006B936 /* base_drag_source.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402C60D92D1390006B936 /* base_drag_source.h */; };
+ 825402D00D92D1390006B936 /* base_drop_target.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C70D92D1390006B936 /* base_drop_target.cc */; };
+ 825402D10D92D1390006B936 /* base_drop_target.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402C80D92D1390006B936 /* base_drop_target.h */; };
+ 825402D20D92D1390006B936 /* base_paths.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402C90D92D1390006B936 /* base_paths.cc */; };
+ 825402D30D92D1390006B936 /* base_paths.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CA0D92D1390006B936 /* base_paths.h */; };
+ 825402D40D92D1390006B936 /* base_switches.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402CB0D92D1390006B936 /* base_switches.cc */; };
+ 825402D50D92D1390006B936 /* base_switches.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CC0D92D1390006B936 /* base_switches.h */; };
+ 825402D60D92D1390006B936 /* basictypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402CD0D92D1390006B936 /* basictypes.h */; };
+ 825402D90D92D15E0006B936 /* blapi.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402D70D92D15E0006B936 /* blapi.h */; };
+ 825402DA0D92D15E0006B936 /* blapit.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402D80D92D15E0006B936 /* blapit.h */; };
+ 825402DF0D92D1730006B936 /* clipboard.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402DB0D92D1730006B936 /* clipboard.h */; };
+ 825402E00D92D1730006B936 /* clipboard.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402DC0D92D1730006B936 /* clipboard.cc */; };
+ 825402E10D92D1730006B936 /* clipboard_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402DD0D92D1730006B936 /* clipboard_util.h */; };
+ 825402E20D92D1730006B936 /* clipboard_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402DE0D92D1730006B936 /* clipboard_util.cc */; };
+ 825402E50D92D1850006B936 /* command_line.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402E30D92D1850006B936 /* command_line.cc */; };
+ 825402E60D92D1850006B936 /* command_line.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402E40D92D1850006B936 /* command_line.h */; };
+ 825402EE0D92D1940006B936 /* condition_variable.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402EB0D92D1940006B936 /* condition_variable.h */; };
+ 825402EF0D92D1940006B936 /* condition_variable.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402EC0D92D1940006B936 /* condition_variable.cc */; };
+ 825402F00D92D1940006B936 /* condition_variable_events.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402ED0D92D1940006B936 /* condition_variable_events.h */; };
+ 825402F50D92D1AC0006B936 /* debug_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402F10D92D1AC0006B936 /* debug_util.h */; };
+ 825402F60D92D1AC0006B936 /* debug_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402F20D92D1AC0006B936 /* debug_util.cc */; };
+ 825402F70D92D1AC0006B936 /* debug_on_start.h in Headers */ = {isa = PBXBuildFile; fileRef = 825402F30D92D1AC0006B936 /* debug_on_start.h */; };
+ 825402F80D92D1AC0006B936 /* debug_on_start.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402F40D92D1AC0006B936 /* debug_on_start.cc */; };
+ 825403010D92D1BC0006B936 /* event_recorder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402FF0D92D1BC0006B936 /* event_recorder.cc */; };
+ 825403020D92D1BC0006B936 /* event_recorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403000D92D1BC0006B936 /* event_recorder.h */; };
+ 825403050D92D1C50006B936 /* file_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403030D92D1C50006B936 /* file_util.h */; };
+ 825403060D92D1C50006B936 /* file_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403040D92D1C50006B936 /* file_util.cc */; };
+ 825403090D92D1CD0006B936 /* file_version_info.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403070D92D1CD0006B936 /* file_version_info.h */; };
+ 8254030A0D92D1CD0006B936 /* file_version_info.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403080D92D1CD0006B936 /* file_version_info.cc */; };
+ 8254030C0D92D1D10006B936 /* fix_wp64.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254030B0D92D1D10006B936 /* fix_wp64.h */; };
+ 8254030E0D92D1DB0006B936 /* fixed_string.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254030D0D92D1DB0006B936 /* fixed_string.h */; };
+ 825403140D92D1E80006B936 /* iat_patch.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254030F0D92D1E80006B936 /* iat_patch.cc */; };
+ 825403150D92D1E80006B936 /* iat_patch.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403100D92D1E80006B936 /* iat_patch.h */; };
+ 825403160D92D1E80006B936 /* icu_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403110D92D1E80006B936 /* icu_util.cc */; };
+ 825403170D92D1E80006B936 /* icu_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403120D92D1E80006B936 /* icu_util.h */; };
+ 825403180D92D1E80006B936 /* id_map.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403130D92D1E80006B936 /* id_map.h */; };
+ 8254031F0D92D1F40006B936 /* image_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403190D92D1F40006B936 /* image_util.cc */; };
+ 825403200D92D1F40006B936 /* image_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031A0D92D1F40006B936 /* image_util.h */; };
+ 825403210D92D1F40006B936 /* json_reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031B0D92D1F40006B936 /* json_reader.cc */; };
+ 825403220D92D1F40006B936 /* json_reader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031C0D92D1F40006B936 /* json_reader.h */; };
+ 825403230D92D1F40006B936 /* json_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254031D0D92D1F40006B936 /* json_writer.cc */; };
+ 825403240D92D1F40006B936 /* json_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254031E0D92D1F40006B936 /* json_writer.h */; };
+ 8254032D0D92D2090006B936 /* lock.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403250D92D2090006B936 /* lock.cc */; };
+ 8254032E0D92D2090006B936 /* lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403260D92D2090006B936 /* lock.h */; };
+ 8254032F0D92D2090006B936 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403270D92D2090006B936 /* logging.cc */; };
+ 825403300D92D2090006B936 /* logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403280D92D2090006B936 /* logging.h */; };
+ 825403310D92D2090006B936 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403290D92D2090006B936 /* md5.cc */; };
+ 825403320D92D2090006B936 /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254032A0D92D2090006B936 /* md5.h */; };
+ 825403330D92D2090006B936 /* memory_debug.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254032B0D92D2090006B936 /* memory_debug.cc */; };
+ 825403340D92D2090006B936 /* memory_debug.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254032C0D92D2090006B936 /* memory_debug.h */; };
+ 825403370D92D2110006B936 /* message_loop.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403350D92D2110006B936 /* message_loop.cc */; };
+ 825403380D92D2110006B936 /* message_loop.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403360D92D2110006B936 /* message_loop.h */; };
+ 825403410D92D2210006B936 /* observer_list.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403390D92D2210006B936 /* observer_list.h */; };
+ 825403420D92D2210006B936 /* path_service.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033A0D92D2210006B936 /* path_service.cc */; };
+ 825403430D92D2210006B936 /* path_service.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033B0D92D2210006B936 /* path_service.h */; };
+ 825403440D92D2210006B936 /* pe_image.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033C0D92D2210006B936 /* pe_image.cc */; };
+ 825403450D92D2210006B936 /* pe_image.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033D0D92D2210006B936 /* pe_image.h */; };
+ 825403460D92D2210006B936 /* pickle.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254033E0D92D2210006B936 /* pickle.cc */; };
+ 825403470D92D2210006B936 /* pickle.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254033F0D92D2210006B936 /* pickle.h */; };
+ 825403480D92D2210006B936 /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403400D92D2210006B936 /* port.h */; };
+ 8254034D0D92D23C0006B936 /* prtypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403490D92D23C0006B936 /* prtypes.h */; };
+ 8254034E0D92D23C0006B936 /* prtime.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254034A0D92D23C0006B936 /* prtime.h */; };
+ 8254034F0D92D23C0006B936 /* prtime.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254034B0D92D23C0006B936 /* prtime.cc */; };
+ 825403500D92D23C0006B936 /* prcpucfg.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254034C0D92D23C0006B936 /* prcpucfg.h */; };
+ 825403530D92D24D0006B936 /* process_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403510D92D24D0006B936 /* process_util.h */; };
+ 825403540D92D24D0006B936 /* process_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403520D92D24D0006B936 /* process_util.cc */; };
+ 825403560D92D2580006B936 /* pure.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403550D92D2580006B936 /* pure.h */; };
+ 825403580D92D25E0006B936 /* pure_api.c in Sources */ = {isa = PBXBuildFile; fileRef = 825403570D92D25E0006B936 /* pure_api.c */; };
+ 825403640D92D27C0006B936 /* ref_counted.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403590D92D27C0006B936 /* ref_counted.h */; };
+ 825403650D92D27C0006B936 /* registry.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035A0D92D27C0006B936 /* registry.cc */; };
+ 825403660D92D27C0006B936 /* registry.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035B0D92D27C0006B936 /* registry.h */; };
+ 825403670D92D27C0006B936 /* resource_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035C0D92D27C0006B936 /* resource_util.cc */; };
+ 825403680D92D27C0006B936 /* resource_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035D0D92D27C0006B936 /* resource_util.h */; };
+ 825403690D92D27C0006B936 /* revocable_store.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254035E0D92D27C0006B936 /* revocable_store.cc */; };
+ 8254036A0D92D27C0006B936 /* revocable_store.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254035F0D92D27C0006B936 /* revocable_store.h */; };
+ 8254036C0D92D27C0006B936 /* scoped_ptr.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403610D92D27C0006B936 /* scoped_ptr.h */; };
+ 8254036D0D92D27C0006B936 /* sha2.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403620D92D27C0006B936 /* sha2.cc */; };
+ 8254036E0D92D27C0006B936 /* sha2.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403630D92D27C0006B936 /* sha2.h */; };
+ 825403710D92D2840006B936 /* sha256.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254036F0D92D2840006B936 /* sha256.h */; };
+ 825403720D92D2840006B936 /* sha512.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403700D92D2840006B936 /* sha512.cc */; };
+ 825403900D92D2CF0006B936 /* shared_event.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403730D92D2CF0006B936 /* shared_event.cc */; };
+ 825403910D92D2CF0006B936 /* shared_event.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403740D92D2CF0006B936 /* shared_event.h */; };
+ 825403920D92D2CF0006B936 /* shared_memory.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403750D92D2CF0006B936 /* shared_memory.cc */; };
+ 825403930D92D2CF0006B936 /* shared_memory.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403760D92D2CF0006B936 /* shared_memory.h */; };
+ 825403940D92D2CF0006B936 /* stack_container.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403770D92D2CF0006B936 /* stack_container.h */; };
+ 825403950D92D2CF0006B936 /* stats_counters.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403780D92D2CF0006B936 /* stats_counters.h */; };
+ 825403960D92D2CF0006B936 /* stats_table.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403790D92D2CF0006B936 /* stats_table.cc */; };
+ 825403970D92D2CF0006B936 /* stats_table.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037A0D92D2CF0006B936 /* stats_table.h */; };
+ 825403980D92D2CF0006B936 /* string_tokenizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037B0D92D2CF0006B936 /* string_tokenizer.h */; };
+ 825403990D92D2CF0006B936 /* string_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254037C0D92D2CF0006B936 /* string_util.cc */; };
+ 8254039A0D92D2CF0006B936 /* string_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037D0D92D2CF0006B936 /* string_util.h */; };
+ 8254039B0D92D2CF0006B936 /* task.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254037E0D92D2CF0006B936 /* task.h */; };
+ 8254039C0D92D2CF0006B936 /* thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254037F0D92D2CF0006B936 /* thread.cc */; };
+ 8254039D0D92D2CF0006B936 /* thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403800D92D2CF0006B936 /* thread.h */; };
+ 8254039E0D92D2CF0006B936 /* thread_local_storage.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403810D92D2CF0006B936 /* thread_local_storage.cc */; };
+ 8254039F0D92D2CF0006B936 /* thread_local_storage.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403820D92D2CF0006B936 /* thread_local_storage.h */; };
+ 825403A00D92D2CF0006B936 /* time.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403830D92D2CF0006B936 /* time.cc */; };
+ 825403A10D92D2CF0006B936 /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403840D92D2CF0006B936 /* time.h */; };
+ 825403A20D92D2CF0006B936 /* timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403850D92D2CF0006B936 /* timer.cc */; };
+ 825403A30D92D2CF0006B936 /* timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403860D92D2CF0006B936 /* timer.h */; };
+ 825403A40D92D2CF0006B936 /* tuple.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403870D92D2CF0006B936 /* tuple.h */; };
+ 825403A50D92D2CF0006B936 /* values.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403880D92D2CF0006B936 /* values.cc */; };
+ 825403A60D92D2CF0006B936 /* values.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403890D92D2CF0006B936 /* values.h */; };
+ 825403A70D92D2CF0006B936 /* win_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038A0D92D2CF0006B936 /* win_util.cc */; };
+ 825403A80D92D2CF0006B936 /* win_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038B0D92D2CF0006B936 /* win_util.h */; };
+ 825403A90D92D2CF0006B936 /* wmi_util.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038C0D92D2CF0006B936 /* wmi_util.cc */; };
+ 825403AA0D92D2CF0006B936 /* wmi_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038D0D92D2CF0006B936 /* wmi_util.h */; };
+ 825403AB0D92D2CF0006B936 /* word_iterator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 8254038E0D92D2CF0006B936 /* word_iterator.cc */; };
+ 825403AC0D92D2CF0006B936 /* word_iterator.h in Headers */ = {isa = PBXBuildFile; fileRef = 8254038F0D92D2CF0006B936 /* word_iterator.h */; };
+ 825403E10D92D31D0006B936 /* bitmap_header.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C00D92D31D0006B936 /* bitmap_header.cc */; };
+ 825403E20D92D31D0006B936 /* bitmap_header.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C10D92D31D0006B936 /* bitmap_header.h */; };
+ 825403E30D92D31D0006B936 /* bitmap_platform_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C20D92D31D0006B936 /* bitmap_platform_device.cc */; };
+ 825403E40D92D31D0006B936 /* bitmap_platform_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C30D92D31D0006B936 /* bitmap_platform_device.h */; };
+ 825403E50D92D31D0006B936 /* font_utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C40D92D31D0006B936 /* font_utils.cc */; };
+ 825403E60D92D31D0006B936 /* font_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C50D92D31D0006B936 /* font_utils.h */; };
+ 825403E70D92D31D0006B936 /* image_resizer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C60D92D31D0006B936 /* image_resizer.cc */; };
+ 825403E80D92D31D0006B936 /* image_resizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C70D92D31D0006B936 /* image_resizer.h */; };
+ 825403E90D92D31D0006B936 /* native_theme.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403C80D92D31D0006B936 /* native_theme.cc */; };
+ 825403EA0D92D31D0006B936 /* native_theme.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403C90D92D31D0006B936 /* native_theme.h */; };
+ 825403EB0D92D31D0006B936 /* platform_canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CA0D92D31D0006B936 /* platform_canvas.h */; };
+ 825403EC0D92D31D0006B936 /* platform_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CB0D92D31D0006B936 /* platform_device.cc */; };
+ 825403ED0D92D31D0006B936 /* platform_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CC0D92D31D0006B936 /* platform_device.h */; };
+ 825403EE0D92D31D0006B936 /* png_decoder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CD0D92D31D0006B936 /* png_decoder.cc */; };
+ 825403EF0D92D31D0006B936 /* png_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403CE0D92D31D0006B936 /* png_decoder.h */; };
+ 825403F00D92D31D0006B936 /* png_encoder.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403CF0D92D31D0006B936 /* png_encoder.cc */; };
+ 825403F10D92D31D0006B936 /* png_encoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D00D92D31D0006B936 /* png_encoder.h */; };
+ 825403F20D92D31D0006B936 /* point.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D10D92D31D0006B936 /* point.cc */; };
+ 825403F30D92D31D0006B936 /* point.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D20D92D31D0006B936 /* point.h */; };
+ 825403F40D92D31D0006B936 /* rect_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D30D92D31D0006B936 /* rect_unittest.cc */; };
+ 825403F50D92D31D0006B936 /* rect.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D40D92D31D0006B936 /* rect.cc */; };
+ 825403F60D92D31D0006B936 /* rect.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D50D92D31D0006B936 /* rect.h */; };
+ 825403F70D92D31D0006B936 /* size.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D60D92D31D0006B936 /* size.cc */; };
+ 825403F80D92D31D0006B936 /* size.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D70D92D31D0006B936 /* size.h */; };
+ 825403F90D92D31D0006B936 /* skia_utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403D80D92D31D0006B936 /* skia_utils.cc */; };
+ 825403FA0D92D31D0006B936 /* skia_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403D90D92D31D0006B936 /* skia_utils.h */; };
+ 825403FB0D92D31D0006B936 /* uniscribe.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DA0D92D31D0006B936 /* uniscribe.cc */; };
+ 825403FC0D92D31D0006B936 /* uniscribe.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DB0D92D31D0006B936 /* uniscribe.h */; };
+ 825403FD0D92D31D0006B936 /* vector_canvas.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DC0D92D31D0006B936 /* vector_canvas.cc */; };
+ 825403FE0D92D31D0006B936 /* vector_canvas.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DD0D92D31D0006B936 /* vector_canvas.h */; };
+ 825403FF0D92D31D0006B936 /* vector_device.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403DE0D92D31D0006B936 /* vector_device.cc */; };
+ 825404000D92D31D0006B936 /* vector_device.h in Headers */ = {isa = PBXBuildFile; fileRef = 825403DF0D92D31D0006B936 /* vector_device.h */; };
+ 825404010D92D31D0006B936 /* platform_canvas.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403E00D92D31D0006B936 /* platform_canvas.cc */; };
+ 82E23FCD0D9C219600F8B40A /* platform_thread.h in Headers */ = {isa = PBXBuildFile; fileRef = 82E23FCB0D9C219600F8B40A /* platform_thread.h */; };
+ 82E23FCE0D9C219600F8B40A /* platform_thread.cc in Sources */ = {isa = PBXBuildFile; fileRef = 82E23FCC0D9C219600F8B40A /* platform_thread.cc */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 825404050D92D33A0006B936 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 825402AA0D92D0C60006B936 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 825402BA0D92D0FA0006B936;
+ remoteInfo = base;
+ };
+ 825404070D92D33C0006B936 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 825402AA0D92D0C60006B936 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 825403B00D92D2E50006B936;
+ remoteInfo = base_gfx;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_cftyperef.h; sourceTree = "<group>"; };
+ 7BD9E84E0DA447F800FC7A01 /* singleton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = singleton.h; sourceTree = "<group>"; };
+ 7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prcpucfg_mac.h; path = third_party/nspr/prcpucfg_mac.h; sourceTree = "<group>"; };
+ 7BEB81490D9B0F33009BA8DD /* time_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time_mac.cc; sourceTree = "<group>"; };
+ 7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util_mac.cc; sourceTree = "<group>"; };
+ 7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_util_mac.h; sourceTree = "<group>"; };
+ 7BEFC29C0D99832D000829AD /* lock_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock_impl.h; sourceTree = "<group>"; };
+ 7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lock_impl_mac.cc; sourceTree = "<group>"; };
+ 821B91680DAABD7F00F350D7 /* string16.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string16.h; sourceTree = "<group>"; };
+ 825402BB0D92D0FA0006B936 /* libbase.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 825402C50D92D1390006B936 /* base_drag_source.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_drag_source.cc; sourceTree = "<group>"; };
+ 825402C60D92D1390006B936 /* base_drag_source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_drag_source.h; sourceTree = "<group>"; };
+ 825402C70D92D1390006B936 /* base_drop_target.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_drop_target.cc; sourceTree = "<group>"; };
+ 825402C80D92D1390006B936 /* base_drop_target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_drop_target.h; sourceTree = "<group>"; };
+ 825402C90D92D1390006B936 /* base_paths.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_paths.cc; sourceTree = "<group>"; };
+ 825402CA0D92D1390006B936 /* base_paths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_paths.h; sourceTree = "<group>"; };
+ 825402CB0D92D1390006B936 /* base_switches.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = base_switches.cc; sourceTree = "<group>"; };
+ 825402CC0D92D1390006B936 /* base_switches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base_switches.h; sourceTree = "<group>"; };
+ 825402CD0D92D1390006B936 /* basictypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = basictypes.h; sourceTree = "<group>"; };
+ 825402D70D92D15E0006B936 /* blapi.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blapi.h; path = third_party/nss/blapi.h; sourceTree = "<group>"; };
+ 825402D80D92D15E0006B936 /* blapit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blapit.h; path = third_party/nss/blapit.h; sourceTree = "<group>"; };
+ 825402DB0D92D1730006B936 /* clipboard.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clipboard.h; sourceTree = "<group>"; };
+ 825402DC0D92D1730006B936 /* clipboard.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = clipboard.cc; sourceTree = "<group>"; };
+ 825402DD0D92D1730006B936 /* clipboard_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = clipboard_util.h; sourceTree = "<group>"; };
+ 825402DE0D92D1730006B936 /* clipboard_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = clipboard_util.cc; sourceTree = "<group>"; };
+ 825402E30D92D1850006B936 /* command_line.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = command_line.cc; sourceTree = "<group>"; };
+ 825402E40D92D1850006B936 /* command_line.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = command_line.h; sourceTree = "<group>"; };
+ 825402EB0D92D1940006B936 /* condition_variable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = condition_variable.h; sourceTree = "<group>"; };
+ 825402EC0D92D1940006B936 /* condition_variable.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = condition_variable.cc; sourceTree = "<group>"; };
+ 825402ED0D92D1940006B936 /* condition_variable_events.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = condition_variable_events.h; sourceTree = "<group>"; };
+ 825402F10D92D1AC0006B936 /* debug_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug_util.h; sourceTree = "<group>"; };
+ 825402F20D92D1AC0006B936 /* debug_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug_util.cc; sourceTree = "<group>"; };
+ 825402F30D92D1AC0006B936 /* debug_on_start.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = debug_on_start.h; sourceTree = "<group>"; };
+ 825402F40D92D1AC0006B936 /* debug_on_start.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = debug_on_start.cc; sourceTree = "<group>"; };
+ 825402FF0D92D1BC0006B936 /* event_recorder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = event_recorder.cc; sourceTree = "<group>"; };
+ 825403000D92D1BC0006B936 /* event_recorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = event_recorder.h; sourceTree = "<group>"; };
+ 825403030D92D1C50006B936 /* file_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_util.h; sourceTree = "<group>"; };
+ 825403040D92D1C50006B936 /* file_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_util.cc; sourceTree = "<group>"; };
+ 825403070D92D1CD0006B936 /* file_version_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_version_info.h; sourceTree = "<group>"; };
+ 825403080D92D1CD0006B936 /* file_version_info.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_version_info.cc; sourceTree = "<group>"; };
+ 8254030B0D92D1D10006B936 /* fix_wp64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fix_wp64.h; sourceTree = "<group>"; };
+ 8254030D0D92D1DB0006B936 /* fixed_string.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixed_string.h; sourceTree = "<group>"; };
+ 8254030F0D92D1E80006B936 /* iat_patch.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = iat_patch.cc; sourceTree = "<group>"; };
+ 825403100D92D1E80006B936 /* iat_patch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iat_patch.h; sourceTree = "<group>"; };
+ 825403110D92D1E80006B936 /* icu_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = icu_util.cc; sourceTree = "<group>"; };
+ 825403120D92D1E80006B936 /* icu_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = icu_util.h; sourceTree = "<group>"; };
+ 825403130D92D1E80006B936 /* id_map.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = id_map.h; sourceTree = "<group>"; };
+ 825403190D92D1F40006B936 /* image_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_util.cc; sourceTree = "<group>"; };
+ 8254031A0D92D1F40006B936 /* image_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_util.h; sourceTree = "<group>"; };
+ 8254031B0D92D1F40006B936 /* json_reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_reader.cc; sourceTree = "<group>"; };
+ 8254031C0D92D1F40006B936 /* json_reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_reader.h; sourceTree = "<group>"; };
+ 8254031D0D92D1F40006B936 /* json_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_writer.cc; sourceTree = "<group>"; };
+ 8254031E0D92D1F40006B936 /* json_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = json_writer.h; sourceTree = "<group>"; };
+ 825403250D92D2090006B936 /* lock.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = lock.cc; sourceTree = "<group>"; };
+ 825403260D92D2090006B936 /* lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lock.h; sourceTree = "<group>"; };
+ 825403270D92D2090006B936 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logging.cc; sourceTree = "<group>"; };
+ 825403280D92D2090006B936 /* logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = logging.h; sourceTree = "<group>"; };
+ 825403290D92D2090006B936 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = "<group>"; };
+ 8254032A0D92D2090006B936 /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = "<group>"; };
+ 8254032B0D92D2090006B936 /* memory_debug.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = memory_debug.cc; sourceTree = "<group>"; };
+ 8254032C0D92D2090006B936 /* memory_debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memory_debug.h; sourceTree = "<group>"; };
+ 825403350D92D2110006B936 /* message_loop.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = message_loop.cc; sourceTree = "<group>"; };
+ 825403360D92D2110006B936 /* message_loop.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = message_loop.h; sourceTree = "<group>"; };
+ 825403390D92D2210006B936 /* observer_list.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = observer_list.h; sourceTree = "<group>"; };
+ 8254033A0D92D2210006B936 /* path_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path_service.cc; sourceTree = "<group>"; };
+ 8254033B0D92D2210006B936 /* path_service.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = path_service.h; sourceTree = "<group>"; };
+ 8254033C0D92D2210006B936 /* pe_image.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pe_image.cc; sourceTree = "<group>"; };
+ 8254033D0D92D2210006B936 /* pe_image.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pe_image.h; sourceTree = "<group>"; };
+ 8254033E0D92D2210006B936 /* pickle.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pickle.cc; sourceTree = "<group>"; };
+ 8254033F0D92D2210006B936 /* pickle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pickle.h; sourceTree = "<group>"; };
+ 825403400D92D2210006B936 /* port.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = port.h; sourceTree = "<group>"; };
+ 825403490D92D23C0006B936 /* prtypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prtypes.h; path = third_party/nspr/prtypes.h; sourceTree = "<group>"; };
+ 8254034A0D92D23C0006B936 /* prtime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prtime.h; path = third_party/nspr/prtime.h; sourceTree = "<group>"; };
+ 8254034B0D92D23C0006B936 /* prtime.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = prtime.cc; path = third_party/nspr/prtime.cc; sourceTree = "<group>"; };
+ 8254034C0D92D23C0006B936 /* prcpucfg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = prcpucfg.h; path = third_party/nspr/prcpucfg.h; sourceTree = "<group>"; };
+ 825403510D92D24D0006B936 /* process_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = process_util.h; sourceTree = "<group>"; };
+ 825403520D92D24D0006B936 /* process_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = process_util.cc; sourceTree = "<group>"; };
+ 825403550D92D2580006B936 /* pure.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pure.h; path = third_party/purify/pure.h; sourceTree = "<group>"; };
+ 825403570D92D25E0006B936 /* pure_api.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pure_api.c; path = third_party/purify/pure_api.c; sourceTree = "<group>"; };
+ 825403590D92D27C0006B936 /* ref_counted.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ref_counted.h; sourceTree = "<group>"; };
+ 8254035A0D92D27C0006B936 /* registry.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = registry.cc; sourceTree = "<group>"; };
+ 8254035B0D92D27C0006B936 /* registry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = registry.h; sourceTree = "<group>"; };
+ 8254035C0D92D27C0006B936 /* resource_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = resource_util.cc; sourceTree = "<group>"; };
+ 8254035D0D92D27C0006B936 /* resource_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resource_util.h; sourceTree = "<group>"; };
+ 8254035E0D92D27C0006B936 /* revocable_store.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = revocable_store.cc; sourceTree = "<group>"; };
+ 8254035F0D92D27C0006B936 /* revocable_store.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = revocable_store.h; sourceTree = "<group>"; };
+ 825403610D92D27C0006B936 /* scoped_ptr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = scoped_ptr.h; sourceTree = "<group>"; };
+ 825403620D92D27C0006B936 /* sha2.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = sha2.cc; sourceTree = "<group>"; };
+ 825403630D92D27C0006B936 /* sha2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = sha2.h; sourceTree = "<group>"; };
+ 8254036F0D92D2840006B936 /* sha256.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sha256.h; path = third_party/nss/sha256.h; sourceTree = "<group>"; };
+ 825403700D92D2840006B936 /* sha512.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sha512.cc; path = third_party/nss/sha512.cc; sourceTree = "<group>"; };
+ 825403730D92D2CF0006B936 /* shared_event.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shared_event.cc; sourceTree = "<group>"; };
+ 825403740D92D2CF0006B936 /* shared_event.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shared_event.h; sourceTree = "<group>"; };
+ 825403750D92D2CF0006B936 /* shared_memory.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = shared_memory.cc; sourceTree = "<group>"; };
+ 825403760D92D2CF0006B936 /* shared_memory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shared_memory.h; sourceTree = "<group>"; };
+ 825403770D92D2CF0006B936 /* stack_container.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_container.h; sourceTree = "<group>"; };
+ 825403780D92D2CF0006B936 /* stats_counters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stats_counters.h; sourceTree = "<group>"; };
+ 825403790D92D2CF0006B936 /* stats_table.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = stats_table.cc; sourceTree = "<group>"; };
+ 8254037A0D92D2CF0006B936 /* stats_table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stats_table.h; sourceTree = "<group>"; };
+ 8254037B0D92D2CF0006B936 /* string_tokenizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_tokenizer.h; sourceTree = "<group>"; };
+ 8254037C0D92D2CF0006B936 /* string_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_util.cc; sourceTree = "<group>"; };
+ 8254037D0D92D2CF0006B936 /* string_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_util.h; sourceTree = "<group>"; };
+ 8254037E0D92D2CF0006B936 /* task.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = task.h; sourceTree = "<group>"; };
+ 8254037F0D92D2CF0006B936 /* thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread.cc; sourceTree = "<group>"; };
+ 825403800D92D2CF0006B936 /* thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread.h; sourceTree = "<group>"; };
+ 825403810D92D2CF0006B936 /* thread_local_storage.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = thread_local_storage.cc; sourceTree = "<group>"; };
+ 825403820D92D2CF0006B936 /* thread_local_storage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = thread_local_storage.h; sourceTree = "<group>"; };
+ 825403830D92D2CF0006B936 /* time.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = time.cc; sourceTree = "<group>"; };
+ 825403840D92D2CF0006B936 /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = time.h; sourceTree = "<group>"; };
+ 825403850D92D2CF0006B936 /* timer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timer.cc; sourceTree = "<group>"; };
+ 825403860D92D2CF0006B936 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = timer.h; sourceTree = "<group>"; };
+ 825403870D92D2CF0006B936 /* tuple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tuple.h; sourceTree = "<group>"; };
+ 825403880D92D2CF0006B936 /* values.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = values.cc; sourceTree = "<group>"; };
+ 825403890D92D2CF0006B936 /* values.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = values.h; sourceTree = "<group>"; };
+ 8254038A0D92D2CF0006B936 /* win_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = win_util.cc; sourceTree = "<group>"; };
+ 8254038B0D92D2CF0006B936 /* win_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = win_util.h; sourceTree = "<group>"; };
+ 8254038C0D92D2CF0006B936 /* wmi_util.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = wmi_util.cc; sourceTree = "<group>"; };
+ 8254038D0D92D2CF0006B936 /* wmi_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wmi_util.h; sourceTree = "<group>"; };
+ 8254038E0D92D2CF0006B936 /* word_iterator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = word_iterator.cc; sourceTree = "<group>"; };
+ 8254038F0D92D2CF0006B936 /* word_iterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = word_iterator.h; sourceTree = "<group>"; };
+ 825403B10D92D2E50006B936 /* libbase_gfx.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libbase_gfx.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ 825403C00D92D31D0006B936 /* bitmap_header.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap_header.cc; sourceTree = "<group>"; };
+ 825403C10D92D31D0006B936 /* bitmap_header.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap_header.h; sourceTree = "<group>"; };
+ 825403C20D92D31D0006B936 /* bitmap_platform_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bitmap_platform_device.cc; sourceTree = "<group>"; };
+ 825403C30D92D31D0006B936 /* bitmap_platform_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bitmap_platform_device.h; sourceTree = "<group>"; };
+ 825403C40D92D31D0006B936 /* font_utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = font_utils.cc; sourceTree = "<group>"; };
+ 825403C50D92D31D0006B936 /* font_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = font_utils.h; sourceTree = "<group>"; };
+ 825403C60D92D31D0006B936 /* image_resizer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = image_resizer.cc; sourceTree = "<group>"; };
+ 825403C70D92D31D0006B936 /* image_resizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = image_resizer.h; sourceTree = "<group>"; };
+ 825403C80D92D31D0006B936 /* native_theme.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = native_theme.cc; sourceTree = "<group>"; };
+ 825403C90D92D31D0006B936 /* native_theme.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = native_theme.h; sourceTree = "<group>"; };
+ 825403CA0D92D31D0006B936 /* platform_canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_canvas.h; sourceTree = "<group>"; };
+ 825403CB0D92D31D0006B936 /* platform_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_device.cc; sourceTree = "<group>"; };
+ 825403CC0D92D31D0006B936 /* platform_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_device.h; sourceTree = "<group>"; };
+ 825403CD0D92D31D0006B936 /* png_decoder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = png_decoder.cc; sourceTree = "<group>"; };
+ 825403CE0D92D31D0006B936 /* png_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png_decoder.h; sourceTree = "<group>"; };
+ 825403CF0D92D31D0006B936 /* png_encoder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = png_encoder.cc; sourceTree = "<group>"; };
+ 825403D00D92D31D0006B936 /* png_encoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = png_encoder.h; sourceTree = "<group>"; };
+ 825403D10D92D31D0006B936 /* point.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = point.cc; sourceTree = "<group>"; };
+ 825403D20D92D31D0006B936 /* point.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = point.h; sourceTree = "<group>"; };
+ 825403D30D92D31D0006B936 /* rect_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect_unittest.cc; sourceTree = "<group>"; };
+ 825403D40D92D31D0006B936 /* rect.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect.cc; sourceTree = "<group>"; };
+ 825403D50D92D31D0006B936 /* rect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = rect.h; sourceTree = "<group>"; };
+ 825403D60D92D31D0006B936 /* size.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = size.cc; sourceTree = "<group>"; };
+ 825403D70D92D31D0006B936 /* size.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = size.h; sourceTree = "<group>"; };
+ 825403D80D92D31D0006B936 /* skia_utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = skia_utils.cc; sourceTree = "<group>"; };
+ 825403D90D92D31D0006B936 /* skia_utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = skia_utils.h; sourceTree = "<group>"; };
+ 825403DA0D92D31D0006B936 /* uniscribe.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = uniscribe.cc; sourceTree = "<group>"; };
+ 825403DB0D92D31D0006B936 /* uniscribe.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uniscribe.h; sourceTree = "<group>"; };
+ 825403DC0D92D31D0006B936 /* vector_canvas.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vector_canvas.cc; sourceTree = "<group>"; };
+ 825403DD0D92D31D0006B936 /* vector_canvas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_canvas.h; sourceTree = "<group>"; };
+ 825403DE0D92D31D0006B936 /* vector_device.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vector_device.cc; sourceTree = "<group>"; };
+ 825403DF0D92D31D0006B936 /* vector_device.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vector_device.h; sourceTree = "<group>"; };
+ 825403E00D92D31D0006B936 /* platform_canvas.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_canvas.cc; sourceTree = "<group>"; };
+ 82E23FCB0D9C219600F8B40A /* platform_thread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = platform_thread.h; sourceTree = "<group>"; };
+ 82E23FCC0D9C219600F8B40A /* platform_thread.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = platform_thread.cc; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 825402B90D92D0FA0006B936 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 825403AF0D92D2E50006B936 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 825402A80D92D0C60006B936 = {
+ isa = PBXGroup;
+ children = (
+ 825402B60D92D0E20006B936 /* base */,
+ 825403B40D92D2EC0006B936 /* base_gfx */,
+ 825402BC0D92D0FA0006B936 /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ 825402B60D92D0E20006B936 /* base */ = {
+ isa = PBXGroup;
+ children = (
+ 825402C50D92D1390006B936 /* base_drag_source.cc */,
+ 825402C60D92D1390006B936 /* base_drag_source.h */,
+ 825402C70D92D1390006B936 /* base_drop_target.cc */,
+ 825402C80D92D1390006B936 /* base_drop_target.h */,
+ 825402C90D92D1390006B936 /* base_paths.cc */,
+ 825402CA0D92D1390006B936 /* base_paths.h */,
+ 825402CB0D92D1390006B936 /* base_switches.cc */,
+ 825402CC0D92D1390006B936 /* base_switches.h */,
+ 825402CD0D92D1390006B936 /* basictypes.h */,
+ 825402D70D92D15E0006B936 /* blapi.h */,
+ 825402D80D92D15E0006B936 /* blapit.h */,
+ 825402DB0D92D1730006B936 /* clipboard.h */,
+ 825402DC0D92D1730006B936 /* clipboard.cc */,
+ 825402DD0D92D1730006B936 /* clipboard_util.h */,
+ 825402DE0D92D1730006B936 /* clipboard_util.cc */,
+ 825402E40D92D1850006B936 /* command_line.h */,
+ 825402E30D92D1850006B936 /* command_line.cc */,
+ 825402EB0D92D1940006B936 /* condition_variable.h */,
+ 825402EC0D92D1940006B936 /* condition_variable.cc */,
+ 825402ED0D92D1940006B936 /* condition_variable_events.h */,
+ 825402F30D92D1AC0006B936 /* debug_on_start.h */,
+ 825402F40D92D1AC0006B936 /* debug_on_start.cc */,
+ 825402F10D92D1AC0006B936 /* debug_util.h */,
+ 825402F20D92D1AC0006B936 /* debug_util.cc */,
+ 825402FF0D92D1BC0006B936 /* event_recorder.cc */,
+ 825403000D92D1BC0006B936 /* event_recorder.h */,
+ 825403030D92D1C50006B936 /* file_util.h */,
+ 825403040D92D1C50006B936 /* file_util.cc */,
+ 825403070D92D1CD0006B936 /* file_version_info.h */,
+ 825403080D92D1CD0006B936 /* file_version_info.cc */,
+ 8254030B0D92D1D10006B936 /* fix_wp64.h */,
+ 8254030D0D92D1DB0006B936 /* fixed_string.h */,
+ 8254030F0D92D1E80006B936 /* iat_patch.cc */,
+ 825403100D92D1E80006B936 /* iat_patch.h */,
+ 825403110D92D1E80006B936 /* icu_util.cc */,
+ 825403120D92D1E80006B936 /* icu_util.h */,
+ 825403130D92D1E80006B936 /* id_map.h */,
+ 825403190D92D1F40006B936 /* image_util.cc */,
+ 8254031A0D92D1F40006B936 /* image_util.h */,
+ 8254031B0D92D1F40006B936 /* json_reader.cc */,
+ 8254031C0D92D1F40006B936 /* json_reader.h */,
+ 8254031D0D92D1F40006B936 /* json_writer.cc */,
+ 8254031E0D92D1F40006B936 /* json_writer.h */,
+ 825403250D92D2090006B936 /* lock.cc */,
+ 825403260D92D2090006B936 /* lock.h */,
+ 7BEFC29C0D99832D000829AD /* lock_impl.h */,
+ 7BEFC29D0D99832D000829AD /* lock_impl_mac.cc */,
+ 825403270D92D2090006B936 /* logging.cc */,
+ 825403280D92D2090006B936 /* logging.h */,
+ 825403290D92D2090006B936 /* md5.cc */,
+ 8254032A0D92D2090006B936 /* md5.h */,
+ 8254032B0D92D2090006B936 /* memory_debug.cc */,
+ 8254032C0D92D2090006B936 /* memory_debug.h */,
+ 825403350D92D2110006B936 /* message_loop.cc */,
+ 825403360D92D2110006B936 /* message_loop.h */,
+ 825403390D92D2210006B936 /* observer_list.h */,
+ 8254033A0D92D2210006B936 /* path_service.cc */,
+ 8254033B0D92D2210006B936 /* path_service.h */,
+ 8254033C0D92D2210006B936 /* pe_image.cc */,
+ 8254033D0D92D2210006B936 /* pe_image.h */,
+ 8254033E0D92D2210006B936 /* pickle.cc */,
+ 8254033F0D92D2210006B936 /* pickle.h */,
+ 82E23FCB0D9C219600F8B40A /* platform_thread.h */,
+ 82E23FCC0D9C219600F8B40A /* platform_thread.cc */,
+ 825403400D92D2210006B936 /* port.h */,
+ 825403490D92D23C0006B936 /* prtypes.h */,
+ 825403510D92D24D0006B936 /* process_util.h */,
+ 825403520D92D24D0006B936 /* process_util.cc */,
+ 8254034A0D92D23C0006B936 /* prtime.h */,
+ 8254034B0D92D23C0006B936 /* prtime.cc */,
+ 8254034C0D92D23C0006B936 /* prcpucfg.h */,
+ 7BEB81100D9AD288009BA8DD /* prcpucfg_mac.h */,
+ 825403550D92D2580006B936 /* pure.h */,
+ 825403570D92D25E0006B936 /* pure_api.c */,
+ 825403590D92D27C0006B936 /* ref_counted.h */,
+ 8254035A0D92D27C0006B936 /* registry.cc */,
+ 8254035B0D92D27C0006B936 /* registry.h */,
+ 8254035C0D92D27C0006B936 /* resource_util.cc */,
+ 8254035D0D92D27C0006B936 /* resource_util.h */,
+ 8254035E0D92D27C0006B936 /* revocable_store.cc */,
+ 8254035F0D92D27C0006B936 /* revocable_store.h */,
+ 7B5AD60D0D9DD8050012BCF1 /* scoped_cftyperef.h */,
+ 825403610D92D27C0006B936 /* scoped_ptr.h */,
+ 825403620D92D27C0006B936 /* sha2.cc */,
+ 825403630D92D27C0006B936 /* sha2.h */,
+ 8254036F0D92D2840006B936 /* sha256.h */,
+ 825403700D92D2840006B936 /* sha512.cc */,
+ 825403730D92D2CF0006B936 /* shared_event.cc */,
+ 825403740D92D2CF0006B936 /* shared_event.h */,
+ 825403750D92D2CF0006B936 /* shared_memory.cc */,
+ 825403760D92D2CF0006B936 /* shared_memory.h */,
+ 7BD9E84E0DA447F800FC7A01 /* singleton.h */,
+ 825403770D92D2CF0006B936 /* stack_container.h */,
+ 825403780D92D2CF0006B936 /* stats_counters.h */,
+ 825403790D92D2CF0006B936 /* stats_table.cc */,
+ 8254037A0D92D2CF0006B936 /* stats_table.h */,
+ 8254037B0D92D2CF0006B936 /* string_tokenizer.h */,
+ 8254037C0D92D2CF0006B936 /* string_util.cc */,
+ 8254037D0D92D2CF0006B936 /* string_util.h */,
+ 7BEB834D0D9C4BE0009BA8DD /* string_util_mac.cc */,
+ 7BEE52C10D9D84FD0067FF23 /* string_util_mac.h */,
+ 821B91680DAABD7F00F350D7 /* string16.h */,
+ 8254037E0D92D2CF0006B936 /* task.h */,
+ 8254037F0D92D2CF0006B936 /* thread.cc */,
+ 825403800D92D2CF0006B936 /* thread.h */,
+ 825403810D92D2CF0006B936 /* thread_local_storage.cc */,
+ 825403820D92D2CF0006B936 /* thread_local_storage.h */,
+ 825403830D92D2CF0006B936 /* time.cc */,
+ 825403840D92D2CF0006B936 /* time.h */,
+ 7BEB81490D9B0F33009BA8DD /* time_mac.cc */,
+ 825403850D92D2CF0006B936 /* timer.cc */,
+ 825403860D92D2CF0006B936 /* timer.h */,
+ 825403870D92D2CF0006B936 /* tuple.h */,
+ 825403880D92D2CF0006B936 /* values.cc */,
+ 825403890D92D2CF0006B936 /* values.h */,
+ 8254038A0D92D2CF0006B936 /* win_util.cc */,
+ 8254038B0D92D2CF0006B936 /* win_util.h */,
+ 8254038C0D92D2CF0006B936 /* wmi_util.cc */,
+ 8254038D0D92D2CF0006B936 /* wmi_util.h */,
+ 8254038E0D92D2CF0006B936 /* word_iterator.cc */,
+ 8254038F0D92D2CF0006B936 /* word_iterator.h */,
+ );
+ name = base;
+ sourceTree = "<group>";
+ };
+ 825402BC0D92D0FA0006B936 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 825402BB0D92D0FA0006B936 /* libbase.a */,
+ 825403B10D92D2E50006B936 /* libbase_gfx.a */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 825403B40D92D2EC0006B936 /* base_gfx */ = {
+ isa = PBXGroup;
+ children = (
+ 825403C00D92D31D0006B936 /* bitmap_header.cc */,
+ 825403C10D92D31D0006B936 /* bitmap_header.h */,
+ 825403C20D92D31D0006B936 /* bitmap_platform_device.cc */,
+ 825403C30D92D31D0006B936 /* bitmap_platform_device.h */,
+ 825403C40D92D31D0006B936 /* font_utils.cc */,
+ 825403C50D92D31D0006B936 /* font_utils.h */,
+ 825403C60D92D31D0006B936 /* image_resizer.cc */,
+ 825403C70D92D31D0006B936 /* image_resizer.h */,
+ 825403C80D92D31D0006B936 /* native_theme.cc */,
+ 825403C90D92D31D0006B936 /* native_theme.h */,
+ 825403CA0D92D31D0006B936 /* platform_canvas.h */,
+ 825403CB0D92D31D0006B936 /* platform_device.cc */,
+ 825403CC0D92D31D0006B936 /* platform_device.h */,
+ 825403CD0D92D31D0006B936 /* png_decoder.cc */,
+ 825403CE0D92D31D0006B936 /* png_decoder.h */,
+ 825403CF0D92D31D0006B936 /* png_encoder.cc */,
+ 825403D00D92D31D0006B936 /* png_encoder.h */,
+ 825403D10D92D31D0006B936 /* point.cc */,
+ 825403D20D92D31D0006B936 /* point.h */,
+ 825403D30D92D31D0006B936 /* rect_unittest.cc */,
+ 825403D40D92D31D0006B936 /* rect.cc */,
+ 825403D50D92D31D0006B936 /* rect.h */,
+ 825403D60D92D31D0006B936 /* size.cc */,
+ 825403D70D92D31D0006B936 /* size.h */,
+ 825403D80D92D31D0006B936 /* skia_utils.cc */,
+ 825403D90D92D31D0006B936 /* skia_utils.h */,
+ 825403DA0D92D31D0006B936 /* uniscribe.cc */,
+ 825403DB0D92D31D0006B936 /* uniscribe.h */,
+ 825403DC0D92D31D0006B936 /* vector_canvas.cc */,
+ 825403DD0D92D31D0006B936 /* vector_canvas.h */,
+ 825403DE0D92D31D0006B936 /* vector_device.cc */,
+ 825403DF0D92D31D0006B936 /* vector_device.h */,
+ 825403E00D92D31D0006B936 /* platform_canvas.cc */,
+ );
+ name = base_gfx;
+ path = gfx;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 825402B70D92D0FA0006B936 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 825402CF0D92D1390006B936 /* base_drag_source.h in Headers */,
+ 825402D10D92D1390006B936 /* base_drop_target.h in Headers */,
+ 825402D30D92D1390006B936 /* base_paths.h in Headers */,
+ 825402D50D92D1390006B936 /* base_switches.h in Headers */,
+ 825402D60D92D1390006B936 /* basictypes.h in Headers */,
+ 825402D90D92D15E0006B936 /* blapi.h in Headers */,
+ 825402DA0D92D15E0006B936 /* blapit.h in Headers */,
+ 825402DF0D92D1730006B936 /* clipboard.h in Headers */,
+ 825402E10D92D1730006B936 /* clipboard_util.h in Headers */,
+ 825402E60D92D1850006B936 /* command_line.h in Headers */,
+ 825402EE0D92D1940006B936 /* condition_variable.h in Headers */,
+ 825402F00D92D1940006B936 /* condition_variable_events.h in Headers */,
+ 825402F50D92D1AC0006B936 /* debug_util.h in Headers */,
+ 825402F70D92D1AC0006B936 /* debug_on_start.h in Headers */,
+ 825403020D92D1BC0006B936 /* event_recorder.h in Headers */,
+ 825403050D92D1C50006B936 /* file_util.h in Headers */,
+ 825403090D92D1CD0006B936 /* file_version_info.h in Headers */,
+ 8254030C0D92D1D10006B936 /* fix_wp64.h in Headers */,
+ 8254030E0D92D1DB0006B936 /* fixed_string.h in Headers */,
+ 825403150D92D1E80006B936 /* iat_patch.h in Headers */,
+ 825403170D92D1E80006B936 /* icu_util.h in Headers */,
+ 825403180D92D1E80006B936 /* id_map.h in Headers */,
+ 825403200D92D1F40006B936 /* image_util.h in Headers */,
+ 825403220D92D1F40006B936 /* json_reader.h in Headers */,
+ 825403240D92D1F40006B936 /* json_writer.h in Headers */,
+ 8254032E0D92D2090006B936 /* lock.h in Headers */,
+ 7BEFC29E0D99832D000829AD /* lock_impl.h in Headers */,
+ 825403300D92D2090006B936 /* logging.h in Headers */,
+ 825403320D92D2090006B936 /* md5.h in Headers */,
+ 825403340D92D2090006B936 /* memory_debug.h in Headers */,
+ 825403380D92D2110006B936 /* message_loop.h in Headers */,
+ 825403410D92D2210006B936 /* observer_list.h in Headers */,
+ 825403430D92D2210006B936 /* path_service.h in Headers */,
+ 825403450D92D2210006B936 /* pe_image.h in Headers */,
+ 825403470D92D2210006B936 /* pickle.h in Headers */,
+ 82E23FCD0D9C219600F8B40A /* platform_thread.h in Headers */,
+ 825403480D92D2210006B936 /* port.h in Headers */,
+ 8254034D0D92D23C0006B936 /* prtypes.h in Headers */,
+ 8254034E0D92D23C0006B936 /* prtime.h in Headers */,
+ 825403500D92D23C0006B936 /* prcpucfg.h in Headers */,
+ 7BEB81110D9AD288009BA8DD /* prcpucfg_mac.h in Headers */,
+ 825403530D92D24D0006B936 /* process_util.h in Headers */,
+ 825403560D92D2580006B936 /* pure.h in Headers */,
+ 825403640D92D27C0006B936 /* ref_counted.h in Headers */,
+ 825403660D92D27C0006B936 /* registry.h in Headers */,
+ 825403680D92D27C0006B936 /* resource_util.h in Headers */,
+ 8254036A0D92D27C0006B936 /* revocable_store.h in Headers */,
+ 7B5AD60E0D9DD8050012BCF1 /* scoped_cftyperef.h in Headers */,
+ 8254036C0D92D27C0006B936 /* scoped_ptr.h in Headers */,
+ 8254036E0D92D27C0006B936 /* sha2.h in Headers */,
+ 825403710D92D2840006B936 /* sha256.h in Headers */,
+ 825403910D92D2CF0006B936 /* shared_event.h in Headers */,
+ 825403930D92D2CF0006B936 /* shared_memory.h in Headers */,
+ 7BD9E84F0DA447F800FC7A01 /* singleton.h in Headers */,
+ 825403940D92D2CF0006B936 /* stack_container.h in Headers */,
+ 825403950D92D2CF0006B936 /* stats_counters.h in Headers */,
+ 825403970D92D2CF0006B936 /* stats_table.h in Headers */,
+ 825403980D92D2CF0006B936 /* string_tokenizer.h in Headers */,
+ 8254039A0D92D2CF0006B936 /* string_util.h in Headers */,
+ 7BEE52C20D9D84FD0067FF23 /* string_util_mac.h in Headers */,
+ 821B91690DAABD7F00F350D7 /* string16.h in Headers */,
+ 8254039B0D92D2CF0006B936 /* task.h in Headers */,
+ 8254039D0D92D2CF0006B936 /* thread.h in Headers */,
+ 8254039F0D92D2CF0006B936 /* thread_local_storage.h in Headers */,
+ 825403A10D92D2CF0006B936 /* time.h in Headers */,
+ 825403A30D92D2CF0006B936 /* timer.h in Headers */,
+ 825403A40D92D2CF0006B936 /* tuple.h in Headers */,
+ 825403A60D92D2CF0006B936 /* values.h in Headers */,
+ 825403A80D92D2CF0006B936 /* win_util.h in Headers */,
+ 825403AA0D92D2CF0006B936 /* wmi_util.h in Headers */,
+ 825403AC0D92D2CF0006B936 /* word_iterator.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 825403AD0D92D2E50006B936 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 825403E20D92D31D0006B936 /* bitmap_header.h in Headers */,
+ 825403E40D92D31D0006B936 /* bitmap_platform_device.h in Headers */,
+ 825403E60D92D31D0006B936 /* font_utils.h in Headers */,
+ 825403E80D92D31D0006B936 /* image_resizer.h in Headers */,
+ 825403EA0D92D31D0006B936 /* native_theme.h in Headers */,
+ 825403EB0D92D31D0006B936 /* platform_canvas.h in Headers */,
+ 825403ED0D92D31D0006B936 /* platform_device.h in Headers */,
+ 825403EF0D92D31D0006B936 /* png_decoder.h in Headers */,
+ 825403F10D92D31D0006B936 /* png_encoder.h in Headers */,
+ 825403F30D92D31D0006B936 /* point.h in Headers */,
+ 825403F60D92D31D0006B936 /* rect.h in Headers */,
+ 825403F80D92D31D0006B936 /* size.h in Headers */,
+ 825403FA0D92D31D0006B936 /* skia_utils.h in Headers */,
+ 825403FC0D92D31D0006B936 /* uniscribe.h in Headers */,
+ 825403FE0D92D31D0006B936 /* vector_canvas.h in Headers */,
+ 825404000D92D31D0006B936 /* vector_device.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 825402BA0D92D0FA0006B936 /* base */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 825402BF0D92D0FB0006B936 /* Build configuration list for PBXNativeTarget "base" */;
+ buildPhases = (
+ 825402B70D92D0FA0006B936 /* Headers */,
+ 825402B80D92D0FA0006B936 /* Sources */,
+ 825402B90D92D0FA0006B936 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = base;
+ productName = base;
+ productReference = 825402BB0D92D0FA0006B936 /* libbase.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ 825403B00D92D2E50006B936 /* base_gfx */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 825403B50D92D2EC0006B936 /* Build configuration list for PBXNativeTarget "base_gfx" */;
+ buildPhases = (
+ 825403AD0D92D2E50006B936 /* Headers */,
+ 825403AE0D92D2E50006B936 /* Sources */,
+ 825403AF0D92D2E50006B936 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = base_gfx;
+ productName = base_gfx;
+ productReference = 825403B10D92D2E50006B936 /* libbase_gfx.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 825402AA0D92D0C60006B936 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 825402AD0D92D0C60006B936 /* Build configuration list for PBXProject "base" */;
+ compatibilityVersion = "Xcode 2.4";
+ hasScannedForEncodings = 0;
+ mainGroup = 825402A80D92D0C60006B936;
+ productRefGroup = 825402BC0D92D0FA0006B936 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 825402BA0D92D0FA0006B936 /* base */,
+ 825403B00D92D2E50006B936 /* base_gfx */,
+ 825404020D92D3340006B936 /* All */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 825402B80D92D0FA0006B936 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 825402CE0D92D1390006B936 /* base_drag_source.cc in Sources */,
+ 825402D00D92D1390006B936 /* base_drop_target.cc in Sources */,
+ 825402D20D92D1390006B936 /* base_paths.cc in Sources */,
+ 825402D40D92D1390006B936 /* base_switches.cc in Sources */,
+ 825402E00D92D1730006B936 /* clipboard.cc in Sources */,
+ 825402E20D92D1730006B936 /* clipboard_util.cc in Sources */,
+ 825402E50D92D1850006B936 /* command_line.cc in Sources */,
+ 825402EF0D92D1940006B936 /* condition_variable.cc in Sources */,
+ 825402F60D92D1AC0006B936 /* debug_util.cc in Sources */,
+ 825402F80D92D1AC0006B936 /* debug_on_start.cc in Sources */,
+ 825403010D92D1BC0006B936 /* event_recorder.cc in Sources */,
+ 825403060D92D1C50006B936 /* file_util.cc in Sources */,
+ 8254030A0D92D1CD0006B936 /* file_version_info.cc in Sources */,
+ 825403140D92D1E80006B936 /* iat_patch.cc in Sources */,
+ 825403160D92D1E80006B936 /* icu_util.cc in Sources */,
+ 8254031F0D92D1F40006B936 /* image_util.cc in Sources */,
+ 825403210D92D1F40006B936 /* json_reader.cc in Sources */,
+ 825403230D92D1F40006B936 /* json_writer.cc in Sources */,
+ 8254032D0D92D2090006B936 /* lock.cc in Sources */,
+ 7BEFC29F0D99832D000829AD /* lock_impl_mac.cc in Sources */,
+ 8254032F0D92D2090006B936 /* logging.cc in Sources */,
+ 825403310D92D2090006B936 /* md5.cc in Sources */,
+ 825403330D92D2090006B936 /* memory_debug.cc in Sources */,
+ 825403370D92D2110006B936 /* message_loop.cc in Sources */,
+ 825403420D92D2210006B936 /* path_service.cc in Sources */,
+ 825403440D92D2210006B936 /* pe_image.cc in Sources */,
+ 825403460D92D2210006B936 /* pickle.cc in Sources */,
+ 82E23FCE0D9C219600F8B40A /* platform_thread.cc in Sources */,
+ 8254034F0D92D23C0006B936 /* prtime.cc in Sources */,
+ 825403540D92D24D0006B936 /* process_util.cc in Sources */,
+ 825403580D92D25E0006B936 /* pure_api.c in Sources */,
+ 825403650D92D27C0006B936 /* registry.cc in Sources */,
+ 825403670D92D27C0006B936 /* resource_util.cc in Sources */,
+ 825403690D92D27C0006B936 /* revocable_store.cc in Sources */,
+ 8254036D0D92D27C0006B936 /* sha2.cc in Sources */,
+ 825403720D92D2840006B936 /* sha512.cc in Sources */,
+ 825403900D92D2CF0006B936 /* shared_event.cc in Sources */,
+ 825403920D92D2CF0006B936 /* shared_memory.cc in Sources */,
+ 825403960D92D2CF0006B936 /* stats_table.cc in Sources */,
+ 825403990D92D2CF0006B936 /* string_util.cc in Sources */,
+ 7BEB834E0D9C4BE0009BA8DD /* string_util_mac.cc in Sources */,
+ 8254039C0D92D2CF0006B936 /* thread.cc in Sources */,
+ 8254039E0D92D2CF0006B936 /* thread_local_storage.cc in Sources */,
+ 825403A00D92D2CF0006B936 /* time.cc in Sources */,
+ 7BEB814A0D9B0F33009BA8DD /* time_mac.cc in Sources */,
+ 825403A20D92D2CF0006B936 /* timer.cc in Sources */,
+ 825403A50D92D2CF0006B936 /* values.cc in Sources */,
+ 825403A70D92D2CF0006B936 /* win_util.cc in Sources */,
+ 825403A90D92D2CF0006B936 /* wmi_util.cc in Sources */,
+ 825403AB0D92D2CF0006B936 /* word_iterator.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 825403AE0D92D2E50006B936 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 825403E10D92D31D0006B936 /* bitmap_header.cc in Sources */,
+ 825403E30D92D31D0006B936 /* bitmap_platform_device.cc in Sources */,
+ 825403E50D92D31D0006B936 /* font_utils.cc in Sources */,
+ 825403E70D92D31D0006B936 /* image_resizer.cc in Sources */,
+ 825403E90D92D31D0006B936 /* native_theme.cc in Sources */,
+ 825403EC0D92D31D0006B936 /* platform_device.cc in Sources */,
+ 825403EE0D92D31D0006B936 /* png_decoder.cc in Sources */,
+ 825403F00D92D31D0006B936 /* png_encoder.cc in Sources */,
+ 825403F20D92D31D0006B936 /* point.cc in Sources */,
+ 825403F40D92D31D0006B936 /* rect_unittest.cc in Sources */,
+ 825403F50D92D31D0006B936 /* rect.cc in Sources */,
+ 825403F70D92D31D0006B936 /* size.cc in Sources */,
+ 825403F90D92D31D0006B936 /* skia_utils.cc in Sources */,
+ 825403FB0D92D31D0006B936 /* uniscribe.cc in Sources */,
+ 825403FD0D92D31D0006B936 /* vector_canvas.cc in Sources */,
+ 825403FF0D92D31D0006B936 /* vector_device.cc in Sources */,
+ 825404010D92D31D0006B936 /* platform_canvas.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 825404060D92D33A0006B936 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 825402BA0D92D0FA0006B936 /* base */;
+ targetProxy = 825404050D92D33A0006B936 /* PBXContainerItemProxy */;
+ };
+ 825404080D92D33C0006B936 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 825403B00D92D2E50006B936 /* base_gfx */;
+ targetProxy = 825404070D92D33C0006B936 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 825402AB0D92D0C60006B936 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_ENABLE_PASCAL_STRINGS = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = DEBUG;
+ USE_HEADERMAP = NO;
+ WARNING_CFLAGS = "-Wall";
+ };
+ name = Debug;
+ };
+ 825402AC0D92D0C60006B936 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ GCC_CW_ASM_SYNTAX = NO;
+ GCC_ENABLE_PASCAL_STRINGS = NO;
+ GCC_PREPROCESSOR_DEFINITIONS = NDEBUG;
+ USE_HEADERMAP = NO;
+ WARNING_CFLAGS = "-Wall";
+ };
+ name = Release;
+ };
+ 825402BD0D92D0FB0006B936 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ HEADER_SEARCH_PATHS = (
+ ..,
+ ../third_party/icu38/public/common,
+ ../third_party/icu38/public/i18n,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = base;
+ };
+ name = Debug;
+ };
+ 825402BE0D92D0FB0006B936 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_DYNAMIC_NO_PIC = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ HEADER_SEARCH_PATHS = (
+ ..,
+ ../third_party/icu38/public/common,
+ ../third_party/icu38/public/i18n,
+ );
+ PREBINDING = NO;
+ PRODUCT_NAME = base;
+ };
+ name = Release;
+ };
+ 825403B20D92D2E50006B936 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ PREBINDING = NO;
+ PRODUCT_NAME = base_gfx;
+ };
+ name = Debug;
+ };
+ 825403B30D92D2E50006B936 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_DYNAMIC_NO_PIC = YES;
+ GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+ PREBINDING = NO;
+ PRODUCT_NAME = base_gfx;
+ };
+ name = Release;
+ };
+ 825404030D92D3340006B936 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ PRODUCT_NAME = All;
+ };
+ name = Debug;
+ };
+ 825404040D92D3340006B936 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ COPY_PHASE_STRIP = YES;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_ENABLE_FIX_AND_CONTINUE = NO;
+ PRODUCT_NAME = All;
+ ZERO_LINK = NO;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 825402AD0D92D0C60006B936 /* Build configuration list for PBXProject "base" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 825402AB0D92D0C60006B936 /* Debug */,
+ 825402AC0D92D0C60006B936 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 825402BF0D92D0FB0006B936 /* Build configuration list for PBXNativeTarget "base" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 825402BD0D92D0FB0006B936 /* Debug */,
+ 825402BE0D92D0FB0006B936 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 825403B50D92D2EC0006B936 /* Build configuration list for PBXNativeTarget "base_gfx" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 825403B20D92D2E50006B936 /* Debug */,
+ 825403B30D92D2E50006B936 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 825404110D92D35C0006B936 /* Build configuration list for PBXAggregateTarget "All" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 825404030D92D3340006B936 /* Debug */,
+ 825404040D92D3340006B936 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 825402AA0D92D0C60006B936 /* Project object */;
+}
diff --git a/base/base_drag_source.cc b/base/base_drag_source.cc
new file mode 100644
index 0000000..daf40c7
--- /dev/null
+++ b/base/base_drag_source.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <atlbase.h>
+
+#include "base/base_drag_source.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, public:
+
+BaseDragSource::BaseDragSource() : ref_count_(0) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, IDropSource implementation:
+
+HRESULT BaseDragSource::QueryContinueDrag(BOOL escape_pressed,
+ DWORD key_state) {
+ if (escape_pressed) {
+ OnDragSourceCancel();
+ return DRAGDROP_S_CANCEL;
+ }
+
+ if (!(key_state & MK_LBUTTON)) {
+ OnDragSourceDrop();
+ return DRAGDROP_S_DROP;
+ }
+
+ OnDragSourceMove();
+ return S_OK;
+}
+
+HRESULT BaseDragSource::GiveFeedback(DWORD effect) {
+ return DRAGDROP_S_USEDEFAULTCURSORS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDragSource, IUnknown implementation:
+
+HRESULT BaseDragSource::QueryInterface(const IID& iid, void** object) {
+ *object = NULL;
+ if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropSource)) {
+ *object = this;
+ } else {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG BaseDragSource::AddRef() {
+ return InterlockedIncrement(&ref_count_);
+}
+
+ULONG BaseDragSource::Release() {
+ if (InterlockedDecrement(&ref_count_) == 0) {
+ ULONG copied_refcnt = ref_count_;
+ delete this;
+ return copied_refcnt;
+ }
+ return ref_count_;
+}
diff --git a/base/base_drag_source.h b/base/base_drag_source.h
new file mode 100644
index 0000000..6c5b25d
--- /dev/null
+++ b/base/base_drag_source.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_DRAG_SOURCE_H__
+#define BASE_BASE_DRAG_SOURCE_H__
+
+#include <objidl.h>
+
+#include "base/basictypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// BaseDragSource
+//
+// A base IDropSource implementation. Handles notifications sent by an active
+// drag-drop operation as the user mouses over other drop targets on their
+// system. This object tells Windows whether or not the drag should continue,
+// and supplies the appropriate cursors.
+//
+class BaseDragSource : public IDropSource {
+ public:
+ BaseDragSource();
+ virtual ~BaseDragSource() { }
+
+ // IDropSource implementation:
+ HRESULT __stdcall QueryContinueDrag(BOOL escape_pressed, DWORD key_state);
+ HRESULT __stdcall GiveFeedback(DWORD effect);
+
+ // IUnknown implementation:
+ HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ protected:
+ virtual void OnDragSourceCancel() { }
+ virtual void OnDragSourceDrop() { }
+ virtual void OnDragSourceMove() { }
+
+ private:
+ LONG ref_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BaseDragSource);
+};
+
+#endif // #ifndef BASE_DRAG_SOURCE_H__
diff --git a/base/base_drop_target.cc b/base/base_drop_target.cc
new file mode 100644
index 0000000..f210381
--- /dev/null
+++ b/base/base_drop_target.cc
@@ -0,0 +1,187 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shlobj.h>
+
+#include "base/base_drop_target.h"
+
+#include "base/logging.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+IDropTargetHelper* BaseDropTarget::cached_drop_target_helper_ = NULL;
+
+BaseDropTarget::BaseDropTarget(HWND hwnd)
+ : suspend_(false),
+ ref_count_(0),
+ hwnd_(hwnd) {
+ DCHECK(hwnd);
+ HRESULT result = RegisterDragDrop(hwnd, this);
+}
+
+BaseDropTarget::~BaseDropTarget() {
+}
+
+// static
+IDropTargetHelper* BaseDropTarget::DropHelper() {
+ if (!cached_drop_target_helper_) {
+ CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER,
+ IID_IDropTargetHelper,
+ reinterpret_cast<void**>(&cached_drop_target_helper_));
+ }
+ return cached_drop_target_helper_;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDropTarget, IDropTarget implementation:
+
+HRESULT BaseDropTarget::DragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect) {
+ // Tell the helper that we entered so it can update the drag image.
+ IDropTargetHelper* drop_helper = DropHelper();
+ if (drop_helper) {
+ drop_helper->DragEnter(GetHWND(), data_object,
+ reinterpret_cast<POINT*>(&cursor_position), *effect);
+ }
+
+ // You can't drag and drop within the same HWND.
+ if (suspend_) {
+ *effect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+ current_data_object_ = data_object;
+ POINT screen_pt = { cursor_position.x, cursor_position.y };
+ *effect = OnDragEnter(current_data_object_, key_state, screen_pt, *effect);
+ return S_OK;
+}
+
+HRESULT BaseDropTarget::DragOver(DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect) {
+ // Tell the helper that we moved over it so it can update the drag image.
+ IDropTargetHelper* drop_helper = DropHelper();
+ if (drop_helper)
+ drop_helper->DragOver(reinterpret_cast<POINT*>(&cursor_position), *effect);
+
+ if (suspend_) {
+ *effect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+
+ POINT screen_pt = { cursor_position.x, cursor_position.y };
+ *effect = OnDragOver(current_data_object_, key_state, screen_pt, *effect);
+ return S_OK;
+}
+
+HRESULT BaseDropTarget::DragLeave() {
+ // Tell the helper that we moved out of it so it can update the drag image.
+ IDropTargetHelper* drop_helper = DropHelper();
+ if (drop_helper)
+ drop_helper->DragLeave();
+
+ OnDragLeave(current_data_object_);
+
+ current_data_object_ = NULL;
+ return S_OK;
+}
+
+HRESULT BaseDropTarget::Drop(IDataObject* data_object,
+ DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect) {
+ // Tell the helper that we dropped onto it so it can update the drag image.
+ IDropTargetHelper* drop_helper = DropHelper();
+ if (drop_helper) {
+ drop_helper->Drop(current_data_object_,
+ reinterpret_cast<POINT*>(&cursor_position), *effect);
+ }
+
+ if (suspend_) {
+ *effect = DROPEFFECT_NONE;
+ return S_OK;
+ }
+
+ POINT screen_pt = { cursor_position.x, cursor_position.y };
+ *effect = OnDrop(current_data_object_, key_state, screen_pt, *effect);
+ return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// BaseDropTarget, IUnknown implementation:
+
+HRESULT BaseDropTarget::QueryInterface(const IID& iid, void** object) {
+ *object = NULL;
+ if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropTarget)) {
+ *object = this;
+ } else {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+}
+
+ULONG BaseDropTarget::AddRef() {
+ return InterlockedIncrement(&ref_count_);
+}
+
+ULONG BaseDropTarget::Release() {
+ if (InterlockedDecrement(&ref_count_) == 0) {
+ ULONG copied_refcnt = ref_count_;
+ delete this;
+ return copied_refcnt;
+ }
+ return ref_count_;
+}
+
+DWORD BaseDropTarget::OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ return DROPEFFECT_NONE;
+}
+
+DWORD BaseDropTarget::OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ return DROPEFFECT_NONE;
+}
+
+void BaseDropTarget::OnDragLeave(IDataObject* data_object) {
+}
+
+DWORD BaseDropTarget::OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect) {
+ return DROPEFFECT_NONE;
+}
diff --git a/base/base_drop_target.h b/base/base_drop_target.h
new file mode 100644
index 0000000..24e8ee5
--- /dev/null
+++ b/base/base_drop_target.h
@@ -0,0 +1,142 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_DROP_TARGET_H__
+#define BASE_BASE_DROP_TARGET_H__
+
+#include <atlbase.h>
+#include <objidl.h>
+#include <shobjidl.h>
+
+#include "base/basictypes.h"
+
+// A DropTarget implementation that takes care of the nitty gritty
+// of dnd. While this class is concrete, subclasses will most likely
+// want to override various OnXXX methods.
+//
+// Because BaseDropTarget is ref counted you shouldn't delete it directly,
+// rather wrap it in a scoped_refptr. Be sure and invoke RevokeDragDrop(m_hWnd)
+// before the HWND is deleted too.
+class BaseDropTarget : public IDropTarget {
+ public:
+ // Create a new BaseDropTarget associating it with the given HWND.
+ explicit BaseDropTarget(HWND hwnd);
+ virtual ~BaseDropTarget();
+
+ // When suspend is set to |true|, the drop target does not receive drops from
+ // drags initiated within the owning HWND.
+ // TODO(beng): (http://b/1085385) figure out how we will handle legitimate
+ // drag-drop operations within the same HWND, such as dragging
+ // selected text to an edit field.
+ void set_suspend(bool suspend) { suspend_ = suspend; }
+
+ // IDropTarget implementation:
+ HRESULT __stdcall DragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect);
+ HRESULT __stdcall DragOver(DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect);
+ HRESULT __stdcall DragLeave();
+ HRESULT __stdcall Drop(IDataObject* data_object,
+ DWORD key_state,
+ POINTL cursor_position,
+ DWORD* effect);
+
+ // IUnknown implementation:
+ HRESULT __stdcall QueryInterface(const IID& iid, void** object);
+ ULONG __stdcall AddRef();
+ ULONG __stdcall Release();
+
+ protected:
+ // Returns the hosting HWND.
+ HWND GetHWND() { return hwnd_; }
+
+ // Invoked when the cursor first moves over the hwnd during a dnd session.
+ // This should return a bitmask of the supported drop operations:
+ // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
+ // DROPEFFECT_MOVE.
+ virtual DWORD OnDragEnter(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ // Invoked when the cursor moves over the window during a dnd session.
+ // This should return a bitmask of the supported drop operations:
+ // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or
+ // DROPEFFECT_MOVE.
+ virtual DWORD OnDragOver(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ // Invoked when the cursor moves outside the bounds of the hwnd during a
+ // dnd session.
+ virtual void OnDragLeave(IDataObject* data_object);
+
+ // Invoked when the drop ends on the window. This should return the operation
+ // that was taken.
+ virtual DWORD OnDrop(IDataObject* data_object,
+ DWORD key_state,
+ POINT cursor_position,
+ DWORD effect);
+
+ private:
+ // Returns the cached drop helper, creating one if necessary. The returned
+ // object is not addrefed. May return NULL if the object couldn't be created.
+ static IDropTargetHelper* DropHelper();
+
+ // The data object currently being dragged over this drop target.
+ CComPtr<IDataObject> current_data_object_;
+
+ // A helper object that is used to provide drag image support while the mouse
+ // is dragging over the content area.
+ //
+ // DO NOT ACCESS DIRECTLY! Use DropHelper() instead, which will lazily create
+ // this if it doesn't exist yet. This object can take tens of milliseconds to
+ // create, and we don't want to block any window opening for this, especially
+ // since often, DnD will never be used. Instead, we force this penalty to the
+ // first time it is actually used.
+ static IDropTargetHelper* cached_drop_target_helper_;
+
+ // The HWND of the source. This HWND is used to determine coordinates for
+ // mouse events that are sent to the renderer notifying various drag states.
+ HWND hwnd_;
+
+ // Whether or not we are currently processing drag notifications for drags
+ // initiated in this window.
+ bool suspend_;
+
+ LONG ref_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BaseDropTarget);
+};
+
+#endif // BASE_BASE_DROP_TARGET_H__
diff --git a/base/base_paths.cc b/base/base_paths.cc
new file mode 100644
index 0000000..389b0bb
--- /dev/null
+++ b/base/base_paths.cc
@@ -0,0 +1,151 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/base_paths.h"
+
+#include <shlobj.h>
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+// This is here for the sole purpose of looking up the corresponding HMODULE.
+static int handle_lookup = 0;
+
+namespace base {
+
+bool PathProvider(int key, std::wstring* result) {
+ // NOTE: DIR_CURRENT is a special cased in PathService::Get
+
+ // We need to go compute the value. It would be nice to support paths with
+ // names longer than MAX_PATH, but the system functions don't seem to be
+ // designed for it either, with the exception of GetTempPath (but other
+ // things will surely break if the temp path is too long, so we don't bother
+ // handling it.
+ wchar_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+
+ std::wstring cur;
+ switch (key) {
+ case base::FILE_EXE:
+ GetModuleFileName(NULL, system_buffer, MAX_PATH);
+ cur = system_buffer;
+ break;
+ case base::FILE_MODULE: {
+ // the resource containing module is assumed to be the one that
+ // this code lives in, whether that's a dll or exe
+ MEMORY_BASIC_INFORMATION info = { 0 };
+ VirtualQuery(reinterpret_cast<void*>(&handle_lookup),
+ &info, sizeof(info));
+ // Module handles are just the allocation base address of the module.
+ HMODULE this_module = reinterpret_cast<HMODULE>(info.AllocationBase);
+ GetModuleFileName(this_module, system_buffer, MAX_PATH);
+ cur = system_buffer;
+ break;
+ }
+ case base::DIR_EXE:
+ PathProvider(base::FILE_EXE, &cur);
+ file_util::TrimFilename(&cur);
+ break;
+ case base::DIR_MODULE:
+ PathProvider(base::FILE_MODULE, &cur);
+ file_util::TrimFilename(&cur);
+ break;
+ case base::DIR_TEMP:
+ if (!file_util::GetTempDir(&cur))
+ return false;
+ break;
+ case base::DIR_WINDOWS:
+ GetWindowsDirectory(system_buffer, MAX_PATH);
+ cur = system_buffer;
+ break;
+ case base::DIR_SYSTEM:
+ GetSystemDirectory(system_buffer, MAX_PATH);
+ cur = system_buffer;
+ break;
+ case base::DIR_PROGRAM_FILES:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ case base::DIR_SOURCE_ROOT:
+ // By default, unit tests execute two levels deep from the source root.
+ // For example: chrome/{Debug|Release}/ui_tests.exe
+ PathProvider(base::DIR_EXE, &cur);
+ file_util::UpOneDirectory(&cur);
+ file_util::UpOneDirectory(&cur);
+ break;
+ case base::DIR_APP_DATA:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
+ system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ case base::DIR_LOCAL_APP_DATA_LOW:
+ // TODO(nsylvain): We should use SHGetKnownFolderPath instead. Bug 1281128
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT,
+ system_buffer)))
+ return false;
+ cur = system_buffer;
+ file_util::UpOneDirectory(&cur);
+ file_util::AppendToPath(&cur, L"LocalLow");
+ break;
+ case base::DIR_LOCAL_APP_DATA:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ case base::DIR_IE_INTERNET_CACHE:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ case base::DIR_COMMON_START_MENU:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ case base::DIR_START_MENU:
+ if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL,
+ SHGFP_TYPE_CURRENT, system_buffer)))
+ return false;
+ cur = system_buffer;
+ break;
+ default:
+ return false;
+ }
+
+ result->swap(cur);
+ return true;
+}
+
+} // namespace base
diff --git a/base/base_paths.h b/base/base_paths.h
new file mode 100644
index 0000000..60a59ca
--- /dev/null
+++ b/base/base_paths.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_BASE_PATHS_H__
+#define BASE_BASE_PATHS_H__
+
+// This file declares path keys for the base module. These can be used with
+// the PathService to access various special directories and files.
+
+namespace base {
+
+enum {
+ PATH_START = 0,
+
+ DIR_CURRENT, // current directory
+ DIR_EXE, // directory containing FILE_EXE
+ DIR_MODULE, // directory containing FILE_MODULE
+ FILE_EXE, // path and filename of the current executable
+ FILE_MODULE, // path and filename of the module containing the code for the
+ // PathService (which could differ from FILE_EXE if the
+ // PathService were compiled into a DLL, for example)
+ DIR_TEMP, // temporary directory
+ DIR_WINDOWS, // Windows directory, usually "c:\windows"
+ DIR_SYSTEM, // Usually c:\windows\system32"
+ DIR_PROGRAM_FILES, // Usually c:\program files
+
+ DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful
+ // for tests that need to locate various resources. It
+ // should not be used outside of test code.
+ DIR_APP_DATA, // Application Data directory under the user profile.
+ DIR_LOCAL_APP_DATA_LOW, // Local AppData directory for low integrity level.
+ DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under the
+ // user profile.
+ DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory.
+ DIR_COMMON_START_MENU, // Usually "C:\Documents and Settings\All Users\
+ // Start Menu\Programs"
+ DIR_START_MENU, // Usually "C:\Documents and Settings\<user>\
+ // Start Menu\Programs"
+ PATH_END
+};
+
+} // namespace base
+
+#endif // BASE_BASE_PATHS_H__
diff --git a/base/base_switches.cc b/base/base_switches.cc
new file mode 100644
index 0000000..8a587da
--- /dev/null
+++ b/base/base_switches.cc
@@ -0,0 +1,58 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/base_switches.h"
+
+namespace switches {
+
+// If the program includes chrome/common/debug_on_start.h, the process will
+// start the JIT system-registered debugger on itself and will wait for 60
+// seconds for the debugger to attach to itself. Then a break point will be hit.
+const wchar_t kDebugOnStart[] = L"debug-on-start";
+
+// Will wait for 60 seconds for a debugger to come to attach to the process.
+const wchar_t kWaitForDebugger[] = L"wait-for-debugger";
+
+// Suppresses all error dialogs when present.
+const wchar_t kNoErrorDialogs[] = L"noerrdialogs";
+
+// Disables the crash reporting.
+const wchar_t kDisableBreakpad[] = L"disable-breakpad";
+
+// Generates full memory crash dump.
+const wchar_t kFullMemoryCrashReport[] = L"full-memory-crash-report";
+
+// The value of this switch determines whether the process is started as a
+// renderer or plugin host. If it's empty, it's the browser.
+const wchar_t kProcessType[] = L"type";
+
+// Enable DCHECKs in release mode.
+const wchar_t kEnableDCHECK[] = L"enable-dcheck";
+
+} // namespace switches
diff --git a/base/base_switches.h b/base/base_switches.h
new file mode 100644
index 0000000..bf64a56
--- /dev/null
+++ b/base/base_switches.h
@@ -0,0 +1,47 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines all the "base" command-line switches.
+
+#ifndef BASE_SWITCHES_H__
+#define BASE_SWITCHES_H__
+
+namespace switches {
+
+extern const wchar_t kDebugOnStart[];
+extern const wchar_t kWaitForDebugger[];
+extern const wchar_t kDisableBreakpad[];
+extern const wchar_t kFullMemoryCrashReport[];
+extern const wchar_t kNoErrorDialogs[];
+extern const wchar_t kProcessType[];
+extern const wchar_t kEnableDCHECK[];
+
+} // namespace switches
+
+#endif // CHROME_COMMON_CHROME_SWITCHES_H__
diff --git a/base/basictypes.h b/base/basictypes.h
new file mode 100644
index 0000000..174fb3e
--- /dev/null
+++ b/base/basictypes.h
@@ -0,0 +1,403 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from base/basictypes.h with some modifications
+
+#ifndef BASE_BASICTYPES_H__
+#define BASE_BASICTYPES_H__
+
+#include <assert.h> // for use with down_cast<>
+#include <limits.h> // So we can set the bounds of our types
+#include <stddef.h> // For size_t
+#include <string.h> // for memcpy
+
+#include "base/port.h" // Types that only need exist on certain systems
+
+typedef signed char schar;
+typedef signed char int8;
+typedef short int16;
+// TODO(mbelshe) Remove these type guards. These are
+// temporary to avoid conflicts with npapi.h.
+#ifndef _INT32
+#define _INT32
+typedef int int32;
+#endif
+typedef long long int64;
+
+// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
+// places. Use the signed types unless your variable represents a bit
+// pattern (eg a hash value) or you really need the extra bit. Do NOT
+// use 'unsigned' to express "this value should always be positive";
+// use assertions for this.
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+// TODO(mbelshe) Remove these type guards. These are
+// temporary to avoid conflicts with npapi.h.
+#ifndef _UINT32
+#define _UINT32
+typedef unsigned int uint32;
+#endif
+typedef unsigned long long uint64;
+
+// A type to represent a Unicode code-point value. As of Unicode 4.0,
+// such values require up to 21 bits.
+// (For type-checking on pointers, make this explicitly signed,
+// and it should always be the signed version of whatever int32 is.)
+typedef signed int char32;
+
+const uint8 kuint8max = UCHAR_MAX;
+const uint16 kuint16max = USHRT_MAX;
+const uint32 kuint32max = UINT_MAX;
+const uint64 kuint64max = ULLONG_MAX;
+const int8 kint8min = SCHAR_MIN;
+const int8 kint8max = SCHAR_MAX;
+const int16 kint16min = SHRT_MIN;
+const int16 kint16max = SHRT_MAX;
+const int32 kint32min = INT_MIN;
+const int32 kint32max = INT_MAX;
+const int64 kint64min = LLONG_MIN;
+const int64 kint64max = LLONG_MAX;
+
+// id for odp categories
+typedef uint32 CatId;
+const CatId kIllegalCatId = static_cast<CatId>(0);
+
+typedef uint32 TermId;
+const TermId kIllegalTermId = static_cast<TermId>(0);
+
+typedef uint32 HostId;
+const HostId kIllegalHostId = static_cast<HostId>(0);
+
+typedef uint32 DomainId;
+const DomainId kIllegalDomainId = static_cast<DomainId>(0);
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// An older, deprecated, politically incorrect name for the above.
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef _MSC_VER
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertable to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+// implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+ return f;
+}
+
+
+// When you upcast (that is, cast a pointer from type Foo to type
+// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts
+// always succeed. When you downcast (that is, cast a pointer from
+// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because
+// how do you know the pointer is really of type SubclassOfFoo? It
+// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus,
+// when you downcast, you should use this macro. In debug mode, we
+// use dynamic_cast<> to double-check the downcast is legal (we die
+// if it's not). In normal mode, we do the efficient static_cast<>
+// instead. Thus, it's important to test in debug mode to make sure
+// the cast is legal!
+// This is the only place in the code we should use dynamic_cast<>.
+// In particular, you SHOULDN'T be using dynamic_cast<> in order to
+// do RTTI (eg code like this:
+// if (dynamic_cast<Subclass1>(foo)) HandleASubclass1Object(foo);
+// if (dynamic_cast<Subclass2>(foo)) HandleASubclass2Object(foo);
+// You should design the code some other way not to need this.
+
+template<typename To, typename From> // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of From *. This test is here only
+ // for compile-time type checking, and has no overhead in an
+ // optimized build at run-time, as it will be optimized away
+ // completely.
+ if (false) {
+ implicit_cast<From*, To>(0);
+ }
+
+ assert(f == NULL || dynamic_cast<To>(f) != NULL); // RTTI: debug mode only!
+ return static_cast<To>(f);
+}
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+template <bool>
+struct CompileAssert {
+};
+
+#undef COMPILE_ASSERT
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+// Implementation details of COMPILE_ASSERT:
+//
+// - COMPILE_ASSERT works by defining an array type that has -1
+// elements (and thus is invalid) when the expression is false.
+//
+// - The simpler definition
+//
+// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
+//
+// does not work, as gcc supports variable-length arrays whose sizes
+// are determined at run-time (this is gcc's extension and not part
+// of the C++ standard). As a result, gcc fails to reject the
+// following code with the simple definition:
+//
+// int foo;
+// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
+// // not a compile-time constant.
+//
+// - By using the type CompileAssert<(bool(expr))>, we ensures that
+// expr is a compile-time constant. (Template arguments must be
+// determined at compile-time.)
+//
+// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
+// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
+//
+// CompileAssert<bool(expr)>
+//
+// instead, these compilers will refuse to compile
+//
+// COMPILE_ASSERT(5 > 0, some_message);
+//
+// (They seem to think the ">" in "5 > 0" marks the end of the
+// template argument list.)
+//
+// - The array size is (bool(expr) ? 1 : -1), instead of simply
+//
+// ((expr) ? 1 : -1).
+//
+// This is to avoid running into a bug in MS VC 7.1, which
+// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
+
+
+// MetatagId refers to metatag-id that we assign to
+// each metatag <name, value> pair..
+typedef uint32 MetatagId;
+
+// Argument type used in interfaces that can optionally take ownership
+// of a passed in argument. If TAKE_OWNERSHIP is passed, the called
+// object takes ownership of the argument. Otherwise it does not.
+enum Ownership {
+ DO_NOT_TAKE_OWNERSHIP,
+ TAKE_OWNERSHIP
+};
+
+// Use these as the mlock_bytes parameter to MLock and MLockGeneral
+enum { MLOCK_ALL = -1, MLOCK_NONE = 0 };
+
+// Helper routine to avoid buggy code like the following:
+// if (pos + N < end) ...
+// If pos is large enough, "pos + N" may overflow. For example,
+// pos==0xfffff000 and N==1MB.
+//
+// This often happens on Nacona's in 32-bit mode, because the
+// main thread's stack is put very close to address 0xffffffff.
+//
+// PointerRangeSize(a,b) returns the size of the range [a,b-1]
+inline size_t PointerRangeSize(const char* start, const char* end) {
+ assert(start <= end);
+ return end - start;
+}
+
+// bit_cast<Dest,Source> is a template function that implements the
+// equivalent of "*reinterpret_cast<Dest*>(&source)". We need this in
+// very low-level functions like the protobuf library and fast math
+// support.
+//
+// float f = 3.14159265358979;
+// int i = bit_cast<int32>(f);
+// // i = 0x40490fdb
+//
+// The classical address-casting method is:
+//
+// // WRONG
+// float f = 3.14159265358979; // WRONG
+// int i = * reinterpret_cast<int*>(&f); // WRONG
+//
+// The address-casting method actually produces undefined behavior
+// according to ISO C++ specification section 3.10 -15 -. Roughly, this
+// section says: if an object in memory has one type, and a program
+// accesses it with a different type, then the result is undefined
+// behavior for most values of "different type".
+//
+// This is true for any cast syntax, either *(int*)&f or
+// *reinterpret_cast<int*>(&f). And it is particularly true for
+// conversions betweeen integral lvalues and floating-point lvalues.
+//
+// The purpose of 3.10 -15- is to allow optimizing compilers to assume
+// that expressions with different types refer to different memory. gcc
+// 4.0.1 has an optimizer that takes advantage of this. So a
+// non-conforming program quietly produces wildly incorrect output.
+//
+// The problem is not the use of reinterpret_cast. The problem is type
+// punning: holding an object in memory of one type and reading its bits
+// back using a different type.
+//
+// The C++ standard is more subtle and complex than this, but that
+// is the basic idea.
+//
+// Anyways ...
+//
+// bit_cast<> calls memcpy() which is blessed by the standard,
+// especially by the example in section 3.9 . Also, of course,
+// bit_cast<> wraps up the nasty logic in one place.
+//
+// Fortunately memcpy() is very fast. In optimized mode, with a
+// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
+// code with the minimal amount of data movement. On a 32-bit system,
+// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
+// compiles to two loads and two stores.
+//
+// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
+//
+// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
+// is likely to surprise you.
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+ // Compile time assertion: sizeof(Dest) == sizeof(Source)
+ // A compile error here means your Dest and Source have different sizes.
+ typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1];
+
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+
+#endif // BASE_BASICTYPES_H__
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
new file mode 100644
index 0000000..0847bbc
--- /dev/null
+++ b/base/build/base.vcproj
@@ -0,0 +1,719 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base"
+ ProjectGUID="{1832A374-8A74-4F9E-B536-69A699B3E165}"
+ RootNamespace="base"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\atomic.h"
+ >
+ </File>
+ <File
+ RelativePath="..\base_drag_source.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\base_drag_source.h"
+ >
+ </File>
+ <File
+ RelativePath="..\base_drop_target.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\base_drop_target.h"
+ >
+ </File>
+ <File
+ RelativePath="..\base_paths.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\base_paths.h"
+ >
+ </File>
+ <File
+ RelativePath="..\base_switches.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\base_switches.h"
+ >
+ </File>
+ <File
+ RelativePath="..\basictypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nss\blapi.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nss\blapit.h"
+ >
+ </File>
+ <File
+ RelativePath="..\clipboard.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\clipboard.h"
+ >
+ </File>
+ <File
+ RelativePath="..\clipboard_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\clipboard_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\command_line.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\command_line.h"
+ >
+ </File>
+ <File
+ RelativePath="..\condition_variable.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\condition_variable.h"
+ >
+ </File>
+ <File
+ RelativePath="..\debug_on_start.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\debug_on_start.h"
+ >
+ </File>
+ <File
+ RelativePath="..\debug_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\debug_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\event_recorder.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\event_recorder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\file_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\file_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\file_version_info.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\file_version_info.h"
+ >
+ </File>
+ <File
+ RelativePath="..\fix_wp64.h"
+ >
+ </File>
+ <File
+ RelativePath="..\fixed_string.h"
+ >
+ </File>
+ <File
+ RelativePath="..\histogram.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\histogram.h"
+ >
+ </File>
+ <File
+ RelativePath="..\hmac.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\hmac.h"
+ >
+ </File>
+ <File
+ RelativePath="..\iat_patch.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\iat_patch.h"
+ >
+ </File>
+ <File
+ RelativePath="..\icu_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\icu_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\id_map.h"
+ >
+ </File>
+ <File
+ RelativePath="..\idle_timer.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\idle_timer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\image_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\image_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\json_reader.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\json_reader.h"
+ >
+ </File>
+ <File
+ RelativePath="..\json_writer.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\json_writer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\linked_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\lock.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\lock.h"
+ >
+ </File>
+ <File
+ RelativePath="..\lock_impl.h"
+ >
+ </File>
+ <File
+ RelativePath="..\lock_impl_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\logging.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\logging.h"
+ >
+ </File>
+ <File
+ RelativePath="..\md5.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\md5.h"
+ >
+ </File>
+ <File
+ RelativePath="..\memory_debug.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\memory_debug.h"
+ >
+ </File>
+ <File
+ RelativePath="..\message_loop.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\message_loop.h"
+ >
+ </File>
+ <File
+ RelativePath="..\non_thread_safe.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\non_thread_safe.h"
+ >
+ </File>
+ <File
+ RelativePath="..\observer_list.h"
+ >
+ </File>
+ <File
+ RelativePath="..\path_service.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\path_service.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pe_image.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\pe_image.h"
+ >
+ </File>
+ <File
+ RelativePath="..\pickle.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\pickle.h"
+ >
+ </File>
+ <File
+ RelativePath="..\platform_thread.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\platform_thread.h"
+ >
+ </File>
+ <File
+ RelativePath="..\port.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nspr\prcpucfg.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nspr\prcpucfg_win.h"
+ >
+ </File>
+ <File
+ RelativePath="..\process.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\process.h"
+ >
+ </File>
+ <File
+ RelativePath="..\process_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\process_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nspr\prtime.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nspr\prtime.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nspr\prtypes.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\purify\pure.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\purify\pure_api.c"
+ >
+ </File>
+ <File
+ RelativePath="..\ref_counted.h"
+ >
+ </File>
+ <File
+ RelativePath="..\registry.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\registry.h"
+ >
+ </File>
+ <File
+ RelativePath="..\resource_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\resource_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\revocable_store.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\revocable_store.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scoped_handle.h"
+ >
+ </File>
+ <File
+ RelativePath="..\scoped_ptr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\sha2.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\sha2.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nss\sha256.h"
+ >
+ </File>
+ <File
+ RelativePath="..\third_party\nss\sha512.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_event.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_event.h"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_memory.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_memory.h"
+ >
+ </File>
+ <File
+ RelativePath="..\singleton.h"
+ >
+ </File>
+ <File
+ RelativePath="..\singleton_internal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\spin_wait.h"
+ >
+ </File>
+ <File
+ RelativePath="..\stack_container.h"
+ >
+ </File>
+ <File
+ RelativePath="..\stats_counters.h"
+ >
+ </File>
+ <File
+ RelativePath="..\stats_table.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\stats_table.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string16.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string_escape.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_escape.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string_piece.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_piece.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string_tokenizer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util_icu.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util_win.h"
+ >
+ </File>
+ <File
+ RelativePath="..\task.h"
+ >
+ </File>
+ <File
+ RelativePath="..\thread.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\thread.h"
+ >
+ </File>
+ <File
+ RelativePath="..\thread_local_storage.h"
+ >
+ </File>
+ <File
+ RelativePath="..\thread_local_storage_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\time.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\time.h"
+ >
+ </File>
+ <File
+ RelativePath="..\time_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\timer.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\timer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\tracked.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\tracked.h"
+ >
+ </File>
+ <File
+ RelativePath="..\tracked_objects.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\tracked_objects.h"
+ >
+ </File>
+ <File
+ RelativePath="..\tuple.h"
+ >
+ </File>
+ <File
+ RelativePath="..\values.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\values.h"
+ >
+ </File>
+ <File
+ RelativePath="..\watchdog.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\watchdog.h"
+ >
+ </File>
+ <File
+ RelativePath="..\win_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\win_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\windows_message_list.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wmi_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wmi_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\word_iterator.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\word_iterator.h"
+ >
+ </File>
+ <File
+ RelativePath="..\worker_pool.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\worker_pool.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/base/build/base.vsprops b/base/build/base.vsprops
new file mode 100644
index 0000000..63e2723
--- /dev/null
+++ b/base/build/base.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/base/build/base_gfx.vcproj b/base/build/base_gfx.vcproj
new file mode 100644
index 0000000..ac60c50
--- /dev/null
+++ b/base/build/base_gfx.vcproj
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base_gfx"
+ ProjectGUID="{A508ADD3-CECE-4E0F-8448-2F5E454DF551}"
+ RootNamespace="base_gfx"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base_gfx.vsprops;..\..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="4"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base_gfx.vsprops;..\..\skia\using_skia.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLibrarianTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\gfx\bitmap_header.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\bitmap_header.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\bitmap_platform_device.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\bitmap_platform_device.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\convolver.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\convolver.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\font_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\font_utils.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\image_operations.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\image_operations.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\native_theme.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\native_theme.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\platform_canvas.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\platform_canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\platform_device.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\platform_device.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\png_decoder.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\png_decoder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\png_encoder.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\png_encoder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\point.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\point.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\rect.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\rect.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\size.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\size.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\skia_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\skia_utils.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\uniscribe.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\uniscribe.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\vector_canvas.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\vector_canvas.h"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\vector_device.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\vector_device.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/base/build/base_gfx.vsprops b/base/build/base_gfx.vsprops
new file mode 100644
index 0000000..9dc5341
--- /dev/null
+++ b/base/build/base_gfx.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base_gfx"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\third_party\icu38\build\using_icu.vsprops;$(SolutionDir)..\third_party\libpng\using_libpng.vsprops;$(SolutionDir)..\third_party\zlib\using_zlib.vsprops;..\..\skia\using_skia.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
new file mode 100644
index 0000000..f9e6787
--- /dev/null
+++ b/base/build/base_unittests.vcproj
@@ -0,0 +1,370 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base_unittests"
+ ProjectGUID="{27A30967-4BBA-48D1-8522-CDE95F7B1CEC}"
+ RootNamespace="base_unittests"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\base_unittests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="UNIT_TEST"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\base_unittests.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="UNIT_TEST"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="support"
+ >
+ <File
+ RelativePath="..\check_handler.h"
+ >
+ </File>
+ <File
+ RelativePath="..\multiprocess_test.h"
+ >
+ </File>
+ <File
+ RelativePath="..\no_windows2000_unittest.h"
+ >
+ </File>
+ <File
+ RelativePath="..\run_all_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\test_suite.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="base_tests"
+ >
+ <File
+ RelativePath="..\atomic_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\check_handler_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\clipboard_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\command_line_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\condition_variable_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\file_util_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\file_version_info_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\fixed_string_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\histogram_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\hmac_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\idletimer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\json_reader_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\json_writer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\linked_ptr_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\message_loop_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\native_theme_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\path_service_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\pe_image_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\pickle_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\pr_time_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\process_util_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\rect_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\ref_counted_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\sha2_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_event_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\shared_memory_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\singleton_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\stack_container_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\stats_table_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_escape_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_piece_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_tokenizer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\string_util_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\thread_local_storage_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\thread_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\time_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\timer_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\tracked_objects_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\values_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\watchdog_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\win_util_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wmi_util_unittest.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="gfx_tests"
+ >
+ <File
+ RelativePath="..\gfx\convolver_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\image_operations_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\platform_canvas_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\png_codec_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\uniscribe_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\gfx\vector_canvas_unittest.cc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/base/build/base_unittests.vsprops b/base/build/base_unittests.vsprops
new file mode 100644
index 0000000..047887d
--- /dev/null
+++ b/base/build/base_unittests.vsprops
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="base_unittests"
+ InheritedPropertySheets=".\base_gfx.vsprops"
+ >
+</VisualStudioPropertySheet>
diff --git a/base/build/debug_message.vcproj b/base/build/debug_message.vcproj
new file mode 100644
index 0000000..8dac295
--- /dev/null
+++ b/base/build/debug_message.vcproj
@@ -0,0 +1,151 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="debug_message"
+ ProjectGUID="{0E5474AC-5996-4B13-87C0-4AE931EE0815}"
+ RootNamespace="DebugMessage"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\debug_message.cc"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/base/build/singleton_dll_unittest.def b/base/build/singleton_dll_unittest.def
new file mode 100644
index 0000000..6ad4d90
--- /dev/null
+++ b/base/build/singleton_dll_unittest.def
@@ -0,0 +1,9 @@
+EXPORTS
+ SingletonInt1
+ SingletonInt2
+ SingletonInt3
+ SingletonInt4
+ SingletonInt5
+ SingletonNoLeak
+ SingletonLeak
+ GetLeakySingleton
diff --git a/base/build/singleton_dll_unittest.vsprops b/base/build/singleton_dll_unittest.vsprops
new file mode 100644
index 0000000..a3eb79b
--- /dev/null
+++ b/base/build/singleton_dll_unittest.vsprops
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="singleton_dll_unittest"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="SINGLETON_UNITTEST_EXPORTS"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ ModuleDefinitionFile="singleton_dll_unittest.def"
+ />
+</VisualStudioPropertySheet>
diff --git a/base/build/singleton_unittest.vcproj b/base/build/singleton_unittest.vcproj
new file mode 100644
index 0000000..6fcb715
--- /dev/null
+++ b/base/build/singleton_unittest.vcproj
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="singleton_dll_unittest"
+ ProjectGUID="{E457F2FB-4708-4001-9B1C-275D7BD7F2A8}"
+ RootNamespace="singleton_unittest"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;.\singleton_dll_unittest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ ConfigurationType="2"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;.\singleton_dll_unittest.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <File
+ RelativePath="..\singleton_dll_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\singleton_dll_unittest.def"
+ >
+ </File>
+ <File
+ RelativePath="..\singleton_dll_unittest.h"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/base/check_handler.h b/base/check_handler.h
new file mode 100644
index 0000000..cf6dd67
--- /dev/null
+++ b/base/check_handler.h
@@ -0,0 +1,112 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_CHECK_HANDLER_H__
+#define BASE_CHECK_HANDLER_H__
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+// This class allows temporary handling of assert firing. When a CHECK()
+// or DCHECK() assertion happens it will in turn generate a SEH exception
+// which can be can captured using a windows SEH hander __try .. _except
+// block. One practical use of this class is for unit tests that make sure
+// CHECK conditions are appropriately handled. For example:
+//
+// TEST(TestGroup, VerifyAssert) {
+// CheckAssertHandler expect_exception;
+// __try {
+// MyClass object; // MyClass dtor will not be called.
+// Param some_bad_param;
+// object.Method(some_bad_param); // Should triggers a CHECK().
+// ADD_FAILURE(); // If we get here the test failed.
+// } __except(EXCEPTION_EXECUTE_HANDLER) {
+// DWORD ecode = GetExceptionCode();
+// EXPECT_EQ(CheckAssertHandler::seh_exception_code(), ecode);
+// }
+// }
+//
+// You can put MyClass outside the __try block so its destructor will be
+// called which could lead to a crash if the state of the object is
+// corrupted by the CHECK you are testing. If that is the case you should
+// fix the state of the object inside the __except block.
+//
+// Since the above code is Windows specific, two helper macros are provided
+// that hide the implementation details. Using the macros the code becomes:
+//
+// TEST(TestGroup, VerifyAssert) {
+// CHECK_HANDLER_BEGIN
+// MyClass object; // MyClass dtor will not be called.
+// Param some_bad_param;
+// object.Method(some_bad_param); // Should triggers a CHECK().
+// CHECK_HANDLER_END
+// }
+//
+// Depending on the compiler settings you might have issue this pragma arround
+// the code that uses this class:
+// #pragma warning(disable: 4509)
+// Which tells the compiler that is ok that some dtors will not be called.
+//
+// Create this object on the stack always.Do not create it inside the
+// __try block itself or the dtor will never be called. Create only one
+// on each scope.
+//
+// The key detail here is the RaiseException() call which transfers
+// program control away from the code that caused the assertion and back
+// into the _except block.
+
+class CheckAssertHandler {
+ public:
+ // Installs the assert handler. The dtor will remove the handler.
+ CheckAssertHandler() {
+ logging::SetLogAssertHandler(&CheckAssertHandler::LogAssertHandler);
+ }
+ ~CheckAssertHandler() {
+ logging::SetLogAssertHandler(NULL);
+ }
+ static DWORD seh_exception_code() { return 0x1765413; }
+ private:
+ static void LogAssertHandler(const std::string&) {
+ ::RaiseException(seh_exception_code(), 0, 0, NULL);
+ }
+};
+
+#define CHECK_HANDLER_BEGIN \
+ CheckAssertHandler chk_ex_handler; \
+ __try {
+
+#define CHECK_HANDLER_END \
+ ADD_FAILURE(); \
+ } __except(EXCEPTION_EXECUTE_HANDLER) { \
+ DWORD ecode = GetExceptionCode(); \
+ EXPECT_EQ(CheckAssertHandler::seh_exception_code(), ecode); \
+ }
+
+#endif // BASE_CHECK_HANDLER_H__
diff --git a/base/check_handler_unittest.cc b/base/check_handler_unittest.cc
new file mode 100644
index 0000000..ba03038
--- /dev/null
+++ b/base/check_handler_unittest.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/check_handler.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SimpleTestClass {
+ public:
+ SimpleTestClass() {
+ }
+ ~SimpleTestClass() {
+ ADD_FAILURE();
+ }
+ void ThisMethodAsserts() {
+ CHECK(false);
+ ADD_FAILURE();
+ }
+};
+
+void ThisFunctionAsserts() {
+ CHECK(false);
+ ADD_FAILURE();
+}
+
+} // namespace
+
+#pragma warning(push)
+#pragma warning(disable: 4509)
+
+TEST(CheckHandlerTest, TestMacroCheckObj) {
+ CHECK_HANDLER_BEGIN
+ SimpleTestClass object;
+ object.ThisMethodAsserts();
+ CHECK_HANDLER_END
+}
+
+TEST(CheckHandlerTest, TestMacroCheckFunc) {
+ CHECK_HANDLER_BEGIN
+ ThisFunctionAsserts();
+ CHECK_HANDLER_END
+}
+
+#pragma warning(pop)
diff --git a/base/clipboard.cc b/base/clipboard.cc
new file mode 100644
index 0000000..b85ec00
--- /dev/null
+++ b/base/clipboard.cc
@@ -0,0 +1,658 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Many of these functions are based on those found in
+// webkit/port/platform/PasteboardWin.cpp
+
+#include <shlobj.h>
+#include <shellapi.h>
+
+#include "base/clipboard.h"
+
+#include "base/clipboard_util.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+
+namespace {
+
+// A small object to ensure we close the clipboard after opening it.
+class ClipboardLock {
+ public:
+ ClipboardLock() : we_own_the_lock_(false) { }
+
+ ~ClipboardLock() {
+ if (we_own_the_lock_)
+ Release();
+ }
+
+ bool Acquire(HWND owner) {
+ // We shouldn't be calling this if we already own the clipbard lock.
+ DCHECK(!we_own_the_lock_);
+
+ // We already have the lock. We don't want to stomp on the other use.
+ if (we_own_the_lock_)
+ return false;
+
+ const int kMaxAttemptsToOpenClipboard = 5;
+
+ // Attempt to acquire the clipboard lock. This may fail if another process
+ // currently holds the lock. We're willing to try a few times in the hopes
+ // of acquiring it.
+ //
+ // This turns out to be an issue when using remote desktop because the
+ // rdpclip.exe process likes to read what we've written to the clipboard and
+ // send it to the RDP client. If we open and close the clipboard in quick
+ // succession, we might be trying to open it while rdpclip.exe has it open,
+ // See Bug 815425.
+ //
+ // In fact, we believe we'll only spin this loop over remote desktop. In
+ // normal situations, the user is initiating clipboard operations and there
+ // shouldn't be lock contention.
+
+ for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) {
+ if (::OpenClipboard(owner)) {
+ we_own_the_lock_ = true;
+ return we_own_the_lock_;
+ }
+
+ // Having failed, we yeild our timeslice to other processes. ::Yield seems
+ // to be insufficient here, so we sleep for 5 ms.
+ if (attempts < (kMaxAttemptsToOpenClipboard - 1))
+ ::Sleep(5);
+ }
+
+ // We failed to acquire the clipboard.
+ return false;
+ }
+
+ void Release() {
+ // We should only be calling this if we already own the clipbard lock.
+ DCHECK(we_own_the_lock_);
+
+ // We we don't have the lock, there is nothing to release.
+ if (!we_own_the_lock_)
+ return;
+
+ ::CloseClipboard();
+ we_own_the_lock_ = false;
+ }
+
+ private:
+ bool we_own_the_lock_;
+};
+
+LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ LRESULT lresult = 0;
+
+ switch(message) {
+ case WM_RENDERFORMAT:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now it's come time to put the data on the clipboard.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_RENDERALLFORMATS:
+ // This message comes when SetClipboardData was sent a null data handle
+ // and now this application is about to quit, so it must put data on
+ // the clipboard before it exits.
+ // We always set data, so there isn't a need to actually do anything here.
+ break;
+ case WM_DRAWCLIPBOARD:
+ break;
+ case WM_DESTROY:
+ break;
+ case WM_CHANGECBCHAIN:
+ break;
+ default:
+ lresult = DefWindowProc(hwnd, message, wparam, lparam);
+ break;
+ }
+ return lresult;
+}
+
+template <typename charT>
+HGLOBAL CreateGlobalData(const std::basic_string<charT>& str) {
+ HGLOBAL data =
+ ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT)));
+ if (data) {
+ charT* raw_data = static_cast<charT*>(::GlobalLock(data));
+ memcpy(raw_data, str.data(), str.size() * sizeof(charT));
+ raw_data[str.size()] = '\0';
+ ::GlobalUnlock(data);
+ }
+ return data;
+};
+
+} // namespace
+
+Clipboard::Clipboard() {
+ // make a dummy HWND to be the clipboard's owner
+ WNDCLASSEX wcex = {0};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = ClipboardOwnerWndProc;
+ wcex.hInstance = GetModuleHandle(NULL);
+ wcex.lpszClassName = L"ClipboardOwnerWindowClass";
+ ::RegisterClassEx(&wcex);
+
+ clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass",
+ L"ClipboardOwnerWindow",
+ 0, 0, 0, 0, 0,
+ HWND_MESSAGE,
+ 0, 0, 0);
+}
+
+Clipboard::~Clipboard() {
+ ::DestroyWindow(clipboard_owner_);
+ clipboard_owner_ = NULL;
+}
+
+void Clipboard::Clear() const {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ ::EmptyClipboard();
+}
+
+void Clipboard::WriteText(const std::wstring& text) const {
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HGLOBAL glob = CreateGlobalData(text);
+ if (glob && !::SetClipboardData(CF_UNICODETEXT, glob))
+ ::GlobalFree(glob);
+}
+
+void Clipboard::WriteHTML(const std::wstring& markup,
+ const std::string& url) const {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ std::string html_fragment;
+ MarkupToHTMLClipboardFormat(markup, url, &html_fragment);
+ HGLOBAL glob = CreateGlobalData(html_fragment);
+ if (glob && !::SetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat,
+ glob)) {
+ ::GlobalFree(glob);
+ }
+}
+
+void Clipboard::WriteBookmark(const std::wstring& title,
+ const std::string& url) const {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ std::wstring bookmark(title);
+ bookmark.append(1, L'\n');
+ bookmark.append(UTF8ToWide(url));
+ HGLOBAL glob = CreateGlobalData(bookmark);
+ if (glob && !::SetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat,
+ glob)) {
+ ::GlobalFree(glob);
+ }
+}
+
+void Clipboard::WriteHyperlink(const std::wstring& title,
+ const std::string& url) const {
+ // Write as a bookmark.
+ WriteBookmark(title, url);
+
+ // Build the HTML link.
+ std::wstring link(L"<a href=\"");
+ link.append(UTF8ToWide(url));
+ link.append(L"\">");
+ link.append(title);
+ link.append(L"</a>");
+
+ // Write as an HTML link.
+ WriteHTML(link, std::string());
+}
+
+void Clipboard::WriteWebSmartPaste() const {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ SetClipboardData(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat, NULL);
+}
+
+void Clipboard::WriteBitmap(const void* pixels, const gfx::Size& size) const {
+ HDC dc = ::GetDC(NULL);
+
+ // This doesn't actually cost us a memcpy when the bitmap comes from the
+ // renderer as we load it into the bitmap using setPixels which just sets a
+ // pointer. Someone has to memcpy it into GDI, it might as well be us here.
+
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size.width();
+ bm_info.bmiHeader.biHeight = -size.height(); // sets vertical orientation
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ // ::CreateDIBSection allocates memory for us to copy our bitmap into.
+ // Unfortunately, we can't write the created bitmap to the clipboard,
+ // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx)
+ void *bits;
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0);
+
+ if (bits && source_hbitmap) {
+ // Copy the bitmap out of shared memory and into GDI
+ memcpy(bits, pixels, 4 * size.width() * size.height());
+
+ // Now we have an HBITMAP, we can write it to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromSharedMemory(const SharedMemory& bitmap,
+ const gfx::Size& size) const {
+ // TODO(darin): share data in gfx/bitmap_header.cc somehow
+ BITMAPINFO bm_info = {0};
+ bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ bm_info.bmiHeader.biWidth = size.width();
+ bm_info.bmiHeader.biHeight = -size.height(); // Sets the vertical orientation
+ bm_info.bmiHeader.biPlanes = 1;
+ bm_info.bmiHeader.biBitCount = 32;
+ bm_info.bmiHeader.biCompression = BI_RGB;
+
+ HDC dc = ::GetDC(NULL);
+
+ // We can create an HBITMAP directly using the shared memory handle, saving
+ // a memcpy.
+ HBITMAP source_hbitmap =
+ ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL, bitmap.handle(), 0);
+
+ if (source_hbitmap) {
+ // Now we can write the HBITMAP to the clipboard
+ WriteBitmapFromHandle(source_hbitmap, size);
+ }
+
+ ::DeleteObject(source_hbitmap);
+ ::ReleaseDC(NULL, dc);
+}
+
+void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap,
+ const gfx::Size& size) const {
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ // We would like to just call ::SetClipboardData on the source_hbitmap,
+ // but that bitmap might not be of a sort we can write to the clipboard.
+ // For this reason, we create a new bitmap, copy the bits over, and then
+ // write that to the clipboard.
+
+ HDC dc = ::GetDC(NULL);
+ HDC compatible_dc = ::CreateCompatibleDC(NULL);
+ HDC source_dc = ::CreateCompatibleDC(NULL);
+
+ // This is the HBITMAP we will eventually write to the clipboard
+ HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height());
+ if (!hbitmap) {
+ // Failed to create the bitmap
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+ return;
+ }
+
+ HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap);
+ HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap);
+
+ // Now we need to blend it into an HBITMAP we can place on the clipboard
+ BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+ ::AlphaBlend(compatible_dc, 0, 0, size.width(), size.height(),
+ source_dc, 0, 0, size.width(), size.height(), bf);
+
+ // Clean up all the handles we just opened
+ ::SelectObject(compatible_dc, old_hbitmap);
+ ::SelectObject(source_dc, old_source);
+ ::DeleteObject(old_hbitmap);
+ ::DeleteObject(old_source);
+ ::DeleteDC(compatible_dc);
+ ::DeleteDC(source_dc);
+ ::ReleaseDC(NULL, dc);
+
+ // Actually write the bitmap to the clipboard
+ ::SetClipboardData(CF_BITMAP, hbitmap);
+}
+
+// Write a file or set of files to the clipboard in HDROP format. When the user
+// invokes a paste command (in a Windows explorer shell, for example), the files
+// will be copied to the paste location.
+void Clipboard::WriteFile(const std::wstring& file) const {
+ std::vector<std::wstring> files;
+ files.push_back(file);
+ WriteFiles(files);
+}
+
+void Clipboard::WriteFiles(const std::vector<std::wstring>& files) const {
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ // Calculate the amount of space we'll need store the strings: require
+ // NULL terminator between strings, and double null terminator at the end.
+ size_t bytes = sizeof(DROPFILES);
+ for (size_t i = 0; i < files.size(); ++i)
+ bytes += (files[i].length() + 1) * sizeof(wchar_t);
+ bytes += sizeof(wchar_t);
+
+ HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes);
+ if (!hdata)
+ return;
+
+ DROPFILES* drop_files = static_cast<DROPFILES*>(::GlobalLock(hdata));
+ drop_files->pFiles = sizeof(DROPFILES);
+ drop_files->fWide = TRUE;
+ BYTE* data = reinterpret_cast<BYTE*>(drop_files) + sizeof(DROPFILES);
+
+ // Copy the strings stored in 'files' with proper NULL separation.
+ wchar_t* data_pos = reinterpret_cast<wchar_t*>(data);
+ for (size_t i = 0; i < files.size(); ++i) {
+ size_t offset = files[i].length() + 1;
+ memcpy(data_pos, files[i].c_str(), offset * sizeof(wchar_t));
+ data_pos += offset;
+ }
+ data_pos[0] = L'\0'; // Double NULL termination after the last string.
+
+ ::GlobalUnlock(hdata);
+ if (!::SetClipboardData(CF_HDROP, hdata))
+ ::GlobalFree(hdata);
+}
+
+bool Clipboard::IsFormatAvailable(unsigned int format) const {
+ return ::IsClipboardFormatAvailable(format) != FALSE;
+}
+
+void Clipboard::ReadText(std::wstring* result) const {
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_UNICODETEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const wchar_t*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadAsciiText(std::string* result) const {
+ if (!result) {
+ NOTREACHED();
+ return;
+ }
+
+ result->clear();
+
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HANDLE data = ::GetClipboardData(CF_TEXT);
+ if (!data)
+ return;
+
+ result->assign(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+}
+
+void Clipboard::ReadHTML(std::wstring* markup, std::string* src_url) const {
+ if (markup)
+ markup->clear();
+
+ if (src_url)
+ src_url->clear();
+
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HANDLE data = ::GetClipboardData(ClipboardUtil::GetHtmlFormat()->cfFormat);
+ if (!data)
+ return;
+
+ std::string html_fragment(static_cast<const char*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ ParseHTMLClipboardFormat(html_fragment, markup, src_url);
+}
+
+void Clipboard::ReadBookmark(std::wstring* title, std::string* url) const {
+ if (title)
+ title->clear();
+
+ if (url)
+ url->clear();
+
+ // Acquire the clipboard.
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HANDLE data = ::GetClipboardData(ClipboardUtil::GetUrlWFormat()->cfFormat);
+ if (!data)
+ return;
+
+ std::wstring bookmark(static_cast<const wchar_t*>(::GlobalLock(data)));
+ ::GlobalUnlock(data);
+
+ ParseBookmarkClipboardFormat(bookmark, title, url);
+}
+
+// Read a file in HDROP format from the clipboard.
+void Clipboard::ReadFile(std::wstring* file) const {
+ if (!file) {
+ NOTREACHED();
+ return;
+ }
+
+ file->clear();
+ std::vector<std::wstring> files;
+ ReadFiles(&files);
+
+ // Take the first file, if available.
+ if (!files.empty())
+ file->assign(files[0]);
+}
+
+// Read a set of files in HDROP format from the clipboard.
+void Clipboard::ReadFiles(std::vector<std::wstring>* files) const {
+ if (!files) {
+ NOTREACHED();
+ return;
+ }
+
+ files->clear();
+
+ ClipboardLock lock;
+ if (!lock.Acquire(clipboard_owner_))
+ return;
+
+ HDROP drop = static_cast<HDROP>(::GetClipboardData(CF_HDROP));
+ if (!drop)
+ return;
+
+ // Count of files in the HDROP.
+ int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0);
+
+ if (count) {
+ for (int i = 0; i < count; ++i) {
+ int size = ::DragQueryFile(drop, i, NULL, 0) + 1;
+ std::wstring file;
+ ::DragQueryFile(drop, i, WriteInto(&file, size), size);
+ files->push_back(file);
+ }
+ }
+}
+
+// static
+void Clipboard::MarkupToHTMLClipboardFormat(const std::wstring& markup,
+ const std::string& src_url,
+ std::string* html_fragment) {
+ DCHECK(html_fragment);
+ // Documentation for the CF_HTML format is available at
+ // http://msdn.microsoft.com/workshop/networking/clipboard/htmlclipboard.asp
+
+ if (markup.empty()) {
+ html_fragment->clear();
+ return;
+ }
+
+ std::string markup_utf8 = WideToUTF8(markup);
+
+ html_fragment->assign("Version:0.9");
+
+ std::string start_html("\nStartHTML:");
+ std::string end_html("\nEndHTML:");
+ std::string start_fragment("\nStartFragment:");
+ std::string end_fragment("\nEndFragment:");
+ std::string source_url("\nSourceURL:");
+
+ bool has_source_url = !src_url.empty() &&
+ !StartsWithASCII(src_url, "about:", false);
+ if (has_source_url)
+ source_url.append(src_url);
+
+ std::string start_markup("\n<HTML>\n<BODY>\n<!--StartFragment-->\n");
+ std::string end_markup("\n<!--EndFragment-->\n</BODY>\n</HTML>");
+
+ // calculate offsets
+ const size_t kMaxDigits = 10; // number of digits in UINT_MAX in base 10
+
+ size_t start_html_offset, start_fragment_offset;
+ size_t end_fragment_offset, end_html_offset;
+
+ start_html_offset = html_fragment->length() +
+ start_html.length() + end_html.length() +
+ start_fragment.length() + end_fragment.length() +
+ (has_source_url ? source_url.length() : 0) +
+ (4*kMaxDigits);
+
+ start_fragment_offset = start_html_offset + start_markup.length();
+ end_fragment_offset = start_fragment_offset + markup_utf8.length();
+ end_html_offset = end_fragment_offset + end_markup.length();
+
+ // fill in needed data
+ start_html.append(StringPrintf("%010u", start_html_offset));
+ end_html.append(StringPrintf("%010u", end_html_offset));
+ start_fragment.append(StringPrintf("%010u", start_fragment_offset));
+ end_fragment.append(StringPrintf("%010u", end_fragment_offset));
+ start_markup.append(markup_utf8);
+
+ // create full html_fragment string from the fragments
+ html_fragment->append(start_html);
+ html_fragment->append(end_html);
+ html_fragment->append(start_fragment);
+ html_fragment->append(end_fragment);
+ if (has_source_url)
+ html_fragment->append(source_url);
+ html_fragment->append(start_markup);
+ html_fragment->append(end_markup);
+}
+
+// static
+void Clipboard::ParseHTMLClipboardFormat(const std::string& html_frag,
+ std::wstring* markup,
+ std::string* src_url) {
+ if (src_url) {
+ // Obtain SourceURL, if present
+ std::string src_url_str("SourceURL:");
+ size_t line_start = html_frag.find(src_url_str, 0);
+ if (line_start != std::string::npos) {
+ size_t src_start = line_start+src_url_str.length();
+ size_t src_end = html_frag.find("\n", line_start);
+
+ if (src_end != std::string::npos)
+ *src_url = html_frag.substr(src_start, src_end - src_start);
+ }
+ }
+
+ if (markup) {
+ // Find the markup between "<!--StartFragment -->" and
+ // "<!--EndFragment -->", accounting for browser quirks
+ size_t markup_start = html_frag.find('<', 0);
+ size_t tag_start = html_frag.find("StartFragment", markup_start);
+ size_t frag_start = html_frag.find('>', tag_start) + 1;
+ // Here we do something slightly differently than WebKit. Webkit does a
+ // forward find for EndFragment, but that seems to be a bug if the html
+ // fragment actually includes the string "EndFragment"
+ size_t tag_end = html_frag.rfind("EndFragment", std::string::npos);
+ size_t frag_end = html_frag.rfind('<', tag_end);
+
+ TrimWhitespace(UTF8ToWide(html_frag.substr(frag_start,
+ frag_end - frag_start)),
+ TRIM_ALL, markup);
+ }
+}
+
+// static
+void Clipboard::ParseBookmarkClipboardFormat(const std::wstring& bookmark,
+ std::wstring* title,
+ std::string* url) {
+ const wchar_t* const kDelim = L"\r\n";
+
+ const size_t title_end = bookmark.find_first_of(kDelim);
+ if (title)
+ title->assign(bookmark.substr(0, title_end));
+
+ if (url) {
+ const size_t url_start = bookmark.find_first_not_of(kDelim, title_end);
+ if (url_start != std::wstring::npos)
+ *url = WideToUTF8(bookmark.substr(url_start, std::wstring::npos));
+ }
+}
diff --git a/base/clipboard.h b/base/clipboard.h
new file mode 100644
index 0000000..3599241
--- /dev/null
+++ b/base/clipboard.h
@@ -0,0 +1,126 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_CLIPBOARD_H__
+#define BASE_CLIPBOARD_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/gfx/size.h"
+#include "base/shared_memory.h"
+
+class Clipboard {
+ public:
+ Clipboard();
+ ~Clipboard();
+
+ // Clears the clipboard. It is usually a good idea to clear the clipboard
+ // before writing content to the clipboard.
+ void Clear() const;
+
+ // Adds UNICODE and ASCII text to the clipboard.
+ void WriteText(const std::wstring& text) const;
+
+ // Adds HTML to the clipboard. The url parameter is optional, but especially
+ // useful if the HTML fragment contains relative links
+ void WriteHTML(const std::wstring& markup, const std::string& src_url) const;
+
+ // Adds a bookmark to the clipboard
+ void WriteBookmark(const std::wstring& title, const std::string& url) const;
+
+ // Adds both a bookmark and an HTML hyperlink to the clipboard. It is a
+ // convenience wrapper around WriteBookmark and WriteHTML.
+ void WriteHyperlink(const std::wstring& title, const std::string& url) const;
+
+ // Adds a bitmap to the clipboard
+ // This is the slowest way to copy a bitmap to the clipboard as we must first
+ // memcpy the pixels into GDI and the blit the bitmap to the clipboard.
+ // Pixel format is assumed to be 32-bit BI_RGB.
+ void WriteBitmap(const void* pixels, const gfx::Size& size) const;
+
+ // Adds a bitmap to the clipboard
+ // This function requires read and write access to the bitmap, but does not
+ // actually modify the shared memory region.
+ // Pixel format is assumed to be 32-bit BI_RGB.
+ void WriteBitmapFromSharedMemory(const SharedMemory& bitmap,
+ const gfx::Size& size) const;
+
+ // Adds a bitmap to the clipboard
+ // This is the fastest way to copy a bitmap to the clipboard. The HBITMAP
+ // may either be device-dependent or device-independent.
+ void WriteBitmapFromHandle(HBITMAP hbitmap, const gfx::Size& size) const;
+
+ // Used by WebKit to determine whether WebKit wrote the clipboard last
+ void WriteWebSmartPaste() const;
+
+ // Adds a file or group of files to the clipboard.
+ void WriteFile(const std::wstring& file) const;
+ void WriteFiles(const std::vector<std::wstring>& files) const;
+
+ // Tests whether the clipboard contains a certain format
+ bool IsFormatAvailable(unsigned int format) const;
+
+ // Reads UNICODE text from the clipboard, if available.
+ void ReadText(std::wstring* result) const;
+
+ // Reads ASCII text from the clipboard, if available.
+ void ReadAsciiText(std::string* result) const;
+
+ // Reads HTML from the clipboard, if available.
+ void ReadHTML(std::wstring* markup, std::string* src_url) const;
+
+ // Reads a bookmark from the clipboard, if available.
+ void ReadBookmark(std::wstring* title, std::string* url) const;
+
+ // Reads a file or group of files from the clipboard, if available, into the
+ // out paramter.
+ void ReadFile(std::wstring* file) const;
+ void ReadFiles(std::vector<std::wstring>* files) const;
+
+ private:
+ static void MarkupToHTMLClipboardFormat(const std::wstring& markup,
+ const std::string& src_url,
+ std::string* html_fragment);
+
+ static void ParseHTMLClipboardFormat(const std::string& html_fragment,
+ std::wstring* markup,
+ std::string* src_url);
+
+ static void ParseBookmarkClipboardFormat(const std::wstring& bookmark,
+ std::wstring* title,
+ std::string* url);
+
+ HWND clipboard_owner_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Clipboard);
+};
+
+#endif // BASE_CLIPBOARD_H__
diff --git a/base/clipboard_unittest.cc b/base/clipboard_unittest.cc
new file mode 100644
index 0000000..219d0a5
--- /dev/null
+++ b/base/clipboard_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/clipboard.h"
+#include "base/clipboard_util.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class ClipboardTest : public testing::Test {
+ };
+}
+
+TEST(ClipboardTest, ClearTest) {
+ Clipboard clipboard;
+
+ clipboard.Clear();
+ EXPECT_EQ(false, clipboard.IsFormatAvailable(CF_TEXT));
+ EXPECT_EQ(false, clipboard.IsFormatAvailable(
+ ClipboardUtil::GetHtmlFormat()->cfFormat));
+}
+
+TEST(ClipboardTest, TextTest) {
+ Clipboard clipboard;
+
+ std::wstring text(L"This is a wstring!#$"), text_result;
+ std::string ascii_text;
+
+ clipboard.Clear();
+ clipboard.WriteText(text);
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_UNICODETEXT));
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_TEXT));
+ clipboard.ReadText(&text_result);
+ EXPECT_EQ(text, text_result);
+ clipboard.ReadAsciiText(&ascii_text);
+ EXPECT_EQ(WideToUTF8(text), ascii_text);
+}
+
+TEST(ClipboardTest, HTMLTest) {
+ Clipboard clipboard;
+
+ std::wstring markup(L"<strong>Hi!</string>"), markup_result;
+ std::string url("http://www.example.com/"), url_result;
+
+ clipboard.Clear();
+ clipboard.WriteHTML(markup, url);
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(
+ ClipboardUtil::GetHtmlFormat()->cfFormat));
+ clipboard.ReadHTML(&markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+ EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, TrickyHTMLTest) {
+ Clipboard clipboard;
+
+ std::wstring markup(L"<em>Bye!<!--EndFragment --></em>"), markup_result;
+ std::string url, url_result;
+
+ clipboard.Clear();
+ clipboard.WriteHTML(markup, url);
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(
+ ClipboardUtil::GetHtmlFormat()->cfFormat));
+ clipboard.ReadHTML(&markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+ EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, BookmarkTest) {
+ Clipboard clipboard;
+
+ std::wstring title(L"The Example Company"), title_result;
+ std::string url("http://www.example.com/"), url_result;
+
+ clipboard.Clear();
+ clipboard.WriteBookmark(title, url);
+ EXPECT_EQ(true,
+ clipboard.IsFormatAvailable(ClipboardUtil::GetUrlWFormat()->cfFormat));
+ clipboard.ReadBookmark(&title_result, &url_result);
+ EXPECT_EQ(title, title_result);
+ EXPECT_EQ(url, url_result);
+}
+
+TEST(ClipboardTest, HyperlinkTest) {
+ Clipboard clipboard;
+
+ std::wstring title(L"The Example Company"), title_result;
+ std::string url("http://www.example.com/"), url_result;
+ std::wstring html(L"<a href=\"http://www.example.com/\">"
+ L"The Example Company</a>"), html_result;
+
+ clipboard.Clear();
+ clipboard.WriteHyperlink(title, url);
+ EXPECT_EQ(true,
+ clipboard.IsFormatAvailable(ClipboardUtil::GetUrlWFormat()->cfFormat));
+ EXPECT_EQ(true,
+ clipboard.IsFormatAvailable(ClipboardUtil::GetHtmlFormat()->cfFormat));
+ clipboard.ReadBookmark(&title_result, &url_result);
+ EXPECT_EQ(title, title_result);
+ EXPECT_EQ(url, url_result);
+ clipboard.ReadHTML(&html_result, &url_result);
+ EXPECT_EQ(html, html_result);
+ //XXX EXPECT_FALSE(url_result.is_valid());
+}
+
+TEST(ClipboardTest, MultiFormatTest) {
+ Clipboard clipboard;
+
+ std::wstring text(L"Hi!"), text_result;
+ std::wstring markup(L"<strong>Hi!</string>"), markup_result;
+ std::string url("http://www.example.com/"), url_result;
+ std::string ascii_text;
+
+ clipboard.Clear();
+ clipboard.WriteHTML(markup, url);
+ clipboard.WriteText(text);
+ EXPECT_EQ(true,
+ clipboard.IsFormatAvailable(ClipboardUtil::GetHtmlFormat()->cfFormat));
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_UNICODETEXT));
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_TEXT));
+ clipboard.ReadHTML(&markup_result, &url_result);
+ EXPECT_EQ(markup, markup_result);
+ EXPECT_EQ(url, url_result);
+ clipboard.ReadText(&text_result);
+ EXPECT_EQ(text, text_result);
+ clipboard.ReadAsciiText(&ascii_text);
+ EXPECT_EQ(WideToUTF8(text), ascii_text);
+}
+
+TEST(ClipboardTest, WebSmartPasteTest) {
+ Clipboard clipboard;
+
+ clipboard.Clear();
+ clipboard.WriteWebSmartPaste();
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(
+ ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat));
+}
+
+TEST(ClipboardTest, BitmapTest) {
+ unsigned int fake_bitmap[] = {
+ 0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89,
+ 0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568,
+ 0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1,
+ };
+
+ Clipboard clipboard;
+
+ clipboard.Clear();
+ clipboard.WriteBitmap(fake_bitmap, gfx::Size(3, 4));
+ EXPECT_EQ(true, clipboard.IsFormatAvailable(CF_BITMAP));
+}
+
+// Files for this test don't actually need to exist on the file system, just
+// don't try to use a non-existent file you've retrieved from the clipboard.
+TEST(ClipboardTest, FileTest) {
+ Clipboard clipboard;
+ clipboard.Clear();
+
+ std::wstring file = L"C:\\Downloads\\My Downloads\\A Special File.txt";
+ clipboard.WriteFile(file);
+ std::wstring out_file;
+ clipboard.ReadFile(&out_file);
+ EXPECT_EQ(file, out_file);
+}
+
+TEST(ClipboardTest, MultipleFilesTest) {
+ Clipboard clipboard;
+ clipboard.Clear();
+
+ std::vector<std::wstring> files;
+ files.push_back(L"C:\\Downloads\\My Downloads\\File 1.exe");
+ files.push_back(L"C:\\Downloads\\My Downloads\\File 2.pdf");
+ files.push_back(L"C:\\Downloads\\My Downloads\\File 3.doc");
+ clipboard.WriteFiles(files);
+
+ std::vector<std::wstring> out_files;
+ clipboard.ReadFiles(&out_files);
+
+ EXPECT_EQ(files.size(), out_files.size());
+ for (size_t i = 0; i < out_files.size(); ++i)
+ EXPECT_EQ(files[i], out_files[i]);
+}
diff --git a/base/clipboard_util.cc b/base/clipboard_util.cc
new file mode 100644
index 0000000..3042e76
--- /dev/null
+++ b/base/clipboard_util.cc
@@ -0,0 +1,400 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/clipboard_util.h"
+
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <wininet.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+
+namespace {
+
+bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url,
+ std::wstring* title) {
+ DCHECK(data_object && url && title);
+
+ STGMEDIUM medium;
+ if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+
+ if (!hdrop)
+ return false;
+
+ bool success = false;
+ wchar_t filename[MAX_PATH];
+ if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) {
+ wchar_t url_buffer[INTERNET_MAX_URL_LENGTH];
+ if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") &&
+ GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer,
+ arraysize(url_buffer), filename)) {
+ *url = url_buffer;
+ PathRemoveExtension(filename);
+ title->assign(PathFindFileName(filename));
+ success = true;
+ }
+ }
+
+ DragFinish(hdrop);
+ GlobalUnlock(medium.hGlobal);
+ // We don't need to call ReleaseStgMedium here because as far as I can tell,
+ // DragFinish frees the hGlobal for us.
+ return success;
+}
+
+bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url,
+ std::wstring* title) {
+ DCHECK(url && title);
+ size_t newline_pos = str.find('\n');
+ bool success = false;
+ if (newline_pos != std::string::npos) {
+ *url = str.substr(0, newline_pos);
+ title->assign(str.substr(newline_pos + 1));
+ success = true;
+ } else {
+ *url = str;
+ title->assign(str);
+ success = true;
+ }
+ return success;
+}
+
+} // namespace
+
+
+FORMATETC* ClipboardUtil::GetUrlFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetUrlWFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetMozUrlFormat() {
+ // The format is "URL\nTitle"
+ static UINT cf = RegisterClipboardFormat(L"text/x-moz-url");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetPlainTextWFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1,
+ TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameWFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFilenameFormat()
+{
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetHtmlFormat() {
+ static UINT cf = RegisterClipboardFormat(L"HTML Format");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetTextHtmlFormat() {
+ static UINT cf = RegisterClipboardFormat(L"text/html");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetCFHDropFormat() {
+ // We don't need to register this format since it's a built in format.
+ static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1,
+ TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileDescriptorFormat() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetFileContentFormatZero() {
+ static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ return &format;
+}
+
+FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() {
+ static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format");
+ static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
+ return &format;
+}
+
+
+bool ClipboardUtil::HasUrl(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFilenameFormat()));
+}
+
+bool ClipboardUtil::HasFilenames(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat()));
+}
+
+bool ClipboardUtil::HasPlainText(IDataObject* data_object) {
+ DCHECK(data_object);
+ return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) ||
+ SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat()));
+}
+
+
+bool ClipboardUtil::GetUrl(IDataObject* data_object,
+ std::wstring* url, std::wstring* title) {
+ DCHECK(data_object && url && title);
+ if (!HasUrl(data_object))
+ return false;
+
+ // Try to extract a URL from |data_object| in a variety of formats.
+ STGMEDIUM store;
+ if (GetUrlFromHDrop(data_object, url, title)) {
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) ||
+ SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) {
+ // Mozilla URL format or unicode URL
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ bool success = SplitUrlAndTitle(data.get(), url, title);
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) {
+ // URL using ascii
+ ScopedHGlobal<char> data(store.hGlobal);
+ bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title);
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) {
+ // filename using unicode
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ bool success = false;
+ if (data.get() && data.get()[0] && (PathFileExists(data.get()) ||
+ PathIsUNC(data.get()))) {
+ wchar_t file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+ if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len,
+ 0))) {
+ *url = file_url;
+ title->assign(file_url);
+ success = true;
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) {
+ // filename using ascii
+ ScopedHGlobal<char> data(store.hGlobal);
+ bool success = false;
+ if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) ||
+ PathIsUNCA(data.get()))) {
+ char file_url[INTERNET_MAX_URL_LENGTH];
+ DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]);
+ if (SUCCEEDED(::UrlCreateFromPathA(data.get(), file_url, &file_url_len, 0))) {
+ *url = UTF8ToWide(file_url);
+ title->assign(*url);
+ success = true;
+ }
+ }
+ ReleaseStgMedium(&store);
+ if (success)
+ return true;
+ }
+
+ return false;
+}
+
+bool ClipboardUtil::GetFilenames(IDataObject* data_object,
+ std::vector<std::wstring>* filenames) {
+ DCHECK(data_object && filenames);
+ if (!HasFilenames(data_object))
+ return false;
+
+ STGMEDIUM medium;
+ if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium)))
+ return false;
+
+ HDROP hdrop = static_cast<HDROP>(GlobalLock(medium.hGlobal));
+ if (!hdrop)
+ return false;
+
+ const int kMaxFilenameLen = 4096;
+ const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0);
+ for (unsigned int i = 0; i < num_files; ++i) {
+ wchar_t filename[kMaxFilenameLen];
+ if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen))
+ continue;
+ filenames->push_back(filename);
+ }
+
+ DragFinish(hdrop);
+ GlobalUnlock(medium.hGlobal);
+ // We don't need to call ReleaseStgMedium here because as far as I can tell,
+ // DragFinish frees the hGlobal for us.
+ return true;
+}
+
+bool ClipboardUtil::GetPlainText(IDataObject* data_object,
+ std::wstring* plain_text) {
+ DCHECK(data_object && plain_text);
+ if (!HasPlainText(data_object))
+ return false;
+
+ STGMEDIUM store;
+ bool success = false;
+ if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) {
+ // Unicode text
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ plain_text->assign(data.get());
+ ReleaseStgMedium(&store);
+ success = true;
+ } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) {
+ // ascii text
+ ScopedHGlobal<char> data(store.hGlobal);
+ plain_text->assign(UTF8ToWide(data.get()));
+ ReleaseStgMedium(&store);
+ success = true;
+ } else {
+ //If a file is dropped on the window, it does not provide either of the
+ //plain text formats, so here we try to forcibly get a url.
+ std::wstring title;
+ success = GetUrl(data_object, plain_text, &title);
+ }
+
+ return success;
+}
+
+bool ClipboardUtil::GetCFHtml(IDataObject* data_object,
+ std::wstring* cf_html) {
+ DCHECK(data_object && cf_html);
+ if (FAILED(data_object->QueryGetData(GetHtmlFormat())))
+ return false;
+
+ STGMEDIUM store;
+ if (FAILED(data_object->GetData(GetHtmlFormat(), &store)))
+ return false;
+
+ // MS CF html
+ ScopedHGlobal<char> data(store.hGlobal);
+ cf_html->assign(UTF8ToWide(std::string(data.get(), data.Size())));
+ ReleaseStgMedium(&store);
+ return true;
+}
+
+bool ClipboardUtil::GetTextHtml(IDataObject* data_object,
+ std::wstring* text_html) {
+ DCHECK(data_object && text_html);
+ if (FAILED(data_object->QueryGetData(GetTextHtmlFormat())))
+ return false;
+
+ STGMEDIUM store;
+ if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store)))
+ return false;
+
+ // raw html
+ ScopedHGlobal<wchar_t> data(store.hGlobal);
+ text_html->assign(data.get());
+ ReleaseStgMedium(&store);
+ return true;
+}
+
+bool ClipboardUtil::GetFileContents(IDataObject* data_object,
+ std::wstring* filename, std::string* file_contents) {
+ DCHECK(data_object && filename && file_contents);
+ bool has_data =
+ SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) ||
+ SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat()));
+
+ if (!has_data)
+ return false;
+
+ STGMEDIUM content;
+ // The call to GetData can be very slow depending on what is in
+ // |data_object|.
+ if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) {
+ if (TYMED_HGLOBAL == content.tymed) {
+ ScopedHGlobal<char> data(content.hGlobal);
+ // The size includes the trailing NULL byte. We don't want it.
+ file_contents->assign(data.get(), data.Size() - 1);
+ }
+ ReleaseStgMedium(&content);
+ }
+
+ STGMEDIUM description;
+ if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(),
+ &description))) {
+ ScopedHGlobal<FILEGROUPDESCRIPTOR> fgd(description.hGlobal);
+ // We expect there to be at least one file in here.
+ DCHECK(fgd->cItems >= 1);
+ filename->assign(fgd->fgd[0].cFileName);
+ ReleaseStgMedium(&description);
+ }
+ return true;
+}
diff --git a/base/clipboard_util.h b/base/clipboard_util.h
new file mode 100644
index 0000000..8daffbb
--- /dev/null
+++ b/base/clipboard_util.h
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Some helper functions for working with the clipboard and IDataObjects.
+
+#include <shlobj.h>
+#include <string>
+#include <vector>
+
+class ClipboardUtil {
+ public:
+ /////////////////////////////////////////////////////////////////////////////
+ // Clipboard formats.
+ static FORMATETC* GetUrlFormat();
+ static FORMATETC* GetUrlWFormat();
+ static FORMATETC* GetMozUrlFormat();
+ static FORMATETC* GetPlainTextFormat();
+ static FORMATETC* GetPlainTextWFormat();
+ static FORMATETC* GetFilenameFormat();
+ static FORMATETC* GetFilenameWFormat();
+ // MS HTML Format
+ static FORMATETC* GetHtmlFormat();
+ // Firefox text/html
+ static FORMATETC* GetTextHtmlFormat();
+ static FORMATETC* GetCFHDropFormat();
+ static FORMATETC* GetFileDescriptorFormat();
+ static FORMATETC* GetFileContentFormatZero();
+ static FORMATETC* GetWebKitSmartPasteFormat();
+
+ /////////////////////////////////////////////////////////////////////////////
+ // These methods check to see if |data_object| has the requested type.
+ // Returns true if it does.
+ static bool HasUrl(IDataObject* data_object);
+ static bool HasFilenames(IDataObject* data_object);
+ static bool HasPlainText(IDataObject* data_object);
+
+ /////////////////////////////////////////////////////////////////////////////
+ // Helper methods to extract information from an IDataObject. These methods
+ // return true if the requested data type is found in |data_object|.
+ static bool GetUrl(IDataObject* data_object,
+ std::wstring* url, std::wstring* title);
+ static bool GetFilenames(IDataObject* data_object,
+ std::vector<std::wstring>* filenames);
+ static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text);
+ static bool GetCFHtml(IDataObject* data_object, std::wstring* cf_html);
+ static bool GetTextHtml(IDataObject* data_object, std::wstring* text_html);
+ static bool GetFileContents(IDataObject* data_object,
+ std::wstring* filename,
+ std::string* file_contents);
+};
diff --git a/base/command_line.cc b/base/command_line.cc
new file mode 100644
index 0000000..7755578
--- /dev/null
+++ b/base/command_line.cc
@@ -0,0 +1,246 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shellapi.h>
+
+#include <algorithm>
+
+#include "base/command_line.h"
+
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+
+using namespace std;
+
+// Since we use a lazy match, make sure that longer versions (like L"--")
+// are listed before shorter versions (like L"-") of similar prefixes.
+const wchar_t* const CommandLine::kSwitchPrefixes[] = {L"--", L"-", L"/"};
+
+const wchar_t CommandLine::kSwitchValueSeparator[] = L"=";
+
+static void Lowercase(wstring* parameter) {
+ transform(parameter->begin(), parameter->end(), parameter->begin(), tolower);
+}
+
+// CommandLine::Data
+//
+// This object holds the parsed data for a command line. We hold this in a
+// separate object from |CommandLine| so that we can share the parsed data
+// across multiple |CommandLine| objects. When we share |Data|, we might be
+// accessing this object on multiple threads. To ensure thread safety, the
+// public interface of this object is const only.
+//
+// Do NOT add any non-const methods to this object. You have been warned.
+class CommandLine::Data {
+ public:
+ Data() {
+ Init(GetCommandLineW());
+ }
+
+ Data(const wstring& command_line) {
+ Init(command_line);
+ }
+
+ const std::wstring& command_line_string() const {
+ return command_line_string_;
+ }
+
+ const std::wstring& program() const {
+ return program_;
+ }
+
+ const std::map<std::wstring, std::wstring>& switches() const {
+ return switches_;
+ }
+
+ const std::vector<std::wstring>& loose_values() const {
+ return loose_values_;
+ }
+
+ private:
+ // Returns true if parameter_string represents a switch. If true,
+ // switch_string and switch_value are set. (If false, both are
+ // set to the empty string.)
+ static bool IsSwitch(const wstring& parameter_string,
+ wstring* switch_string,
+ wstring* switch_value) {
+
+ *switch_string = L"";
+ *switch_value = L"";
+
+ for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) {
+ std::wstring prefix(kSwitchPrefixes[i]);
+ if (parameter_string.find(prefix) != 0) // check prefix
+ continue;
+
+ const size_t switch_start = prefix.length();
+ const size_t equals_position = parameter_string.find(
+ kSwitchValueSeparator, switch_start);
+ if (equals_position == wstring::npos) {
+ *switch_string = parameter_string.substr(switch_start);
+ } else {
+ *switch_string = parameter_string.substr(
+ switch_start, equals_position - switch_start);
+ *switch_value = parameter_string.substr(equals_position + 1);
+ }
+ Lowercase(switch_string);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ // Does the actual parsing of the command line.
+ void Init(const std::wstring& command_line) {
+ TrimWhitespace(command_line, TRIM_ALL, &command_line_string_);
+
+ if (command_line_string_.empty())
+ return;
+
+ int num_args = 0;
+ wchar_t** args = NULL;
+
+ args = CommandLineToArgvW(command_line_string_.c_str(), &num_args);
+
+ // Populate program_ with the trimmed version of the first arg.
+ TrimWhitespace(args[0], TRIM_ALL, &program_);
+
+ for (int i = 1; i < num_args; ++i) {
+ wstring arg;
+ TrimWhitespace(args[i], TRIM_ALL, &arg);
+
+ wstring switch_string;
+ wstring switch_value;
+ if (IsSwitch(arg, &switch_string, &switch_value)) {
+ switches_[switch_string] = switch_value;
+ } else {
+ loose_values_.push_back(arg);
+ }
+ }
+
+ if (args)
+ LocalFree(args);
+ }
+
+ std::wstring command_line_string_;
+ std::wstring program_;
+ std::map<std::wstring, std::wstring> switches_;
+ std::vector<std::wstring> loose_values_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CommandLine::Data);
+};
+
+CommandLine::CommandLine()
+ : we_own_data_(false), // The Singleton class will manage it for us.
+ data_(Singleton<Data>::get()) {
+}
+
+CommandLine::CommandLine(const wstring& command_line)
+ : we_own_data_(true),
+ data_(new Data(command_line)) {
+}
+
+CommandLine::~CommandLine() {
+ if (we_own_data_)
+ delete data_;
+}
+
+bool CommandLine::HasSwitch(const wstring& switch_string) const {
+ wstring lowercased_switch(switch_string);
+ Lowercase(&lowercased_switch);
+ return data_->switches().find(lowercased_switch) != data_->switches().end();
+}
+
+wstring CommandLine::GetSwitchValue(const wstring& switch_string) const {
+ wstring lowercased_switch(switch_string);
+ Lowercase(&lowercased_switch);
+
+ const map<wstring, wstring>::const_iterator result =
+ data_->switches().find(lowercased_switch);
+
+ if (result == data_->switches().end()) {
+ return L"";
+ } else {
+ return result->second;
+ }
+}
+
+size_t CommandLine::GetLooseValueCount() const {
+ return data_->loose_values().size();
+}
+
+CommandLine::LooseValueIterator CommandLine::GetLooseValuesBegin() const {
+ return data_->loose_values().begin();
+}
+
+CommandLine::LooseValueIterator CommandLine::GetLooseValuesEnd() const {
+ return data_->loose_values().end();
+}
+
+std::wstring CommandLine::command_line_string() const {
+ return data_->command_line_string();
+}
+
+std::wstring CommandLine::program() const {
+ return data_->program();
+}
+
+// static
+void CommandLine::AppendSwitch(wstring* command_line_string,
+ const wstring& switch_string) {
+ DCHECK(command_line_string);
+ command_line_string->append(L" ");
+ command_line_string->append(kSwitchPrefixes[0]);
+ command_line_string->append(switch_string);
+}
+
+// static
+void CommandLine::AppendSwitchWithValue(wstring* command_line_string,
+ const wstring& switch_string,
+ const wstring& value_string) {
+ AppendSwitch(command_line_string, switch_string);
+
+ if (value_string.empty())
+ return;
+
+ command_line_string->append(kSwitchValueSeparator);
+ // NOTE(jhughes): If the value contains a quotation mark at one
+ // end but not both, you may get unusable output.
+ if ((value_string.find(L" ") != std::wstring::npos) &&
+ (value_string[0] != L'"') &&
+ (value_string[value_string.length() - 1] != L'"')) {
+ // need to provide quotes
+ StringAppendF(command_line_string, L"\"%s\"", value_string.c_str());
+ } else {
+ command_line_string->append(value_string);
+ }
+}
diff --git a/base/command_line.h b/base/command_line.h
new file mode 100644
index 0000000..f1b7ade
--- /dev/null
+++ b/base/command_line.h
@@ -0,0 +1,119 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file contains a class that can be used to extract the salient
+// elements of a command line in a relatively lightweight manner.
+// Switches can optionally have a value attached using an equals sign,
+// as in "-switch=value". Arguments that aren't prefixed with a
+// switch prefix are considered "loose parameters". Switch names
+// are case-insensitive.
+
+#ifndef BASE_COMMAND_LINE_H__
+#define BASE_COMMAND_LINE_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+class CommandLine {
+ public:
+ // Creates a parsed version of the command line used to launch
+ // the current process.
+ CommandLine();
+
+ // Creates a parsed version of the given command-line string.
+ // The program name is assumed to be the first item in the string.
+ CommandLine(const std::wstring& command_line);
+
+ ~CommandLine();
+
+ // Returns true if this command line contains the given switch.
+ // (Switch names are case-insensitive.)
+ bool HasSwitch(const std::wstring& switch_string) const;
+
+ // Returns the value associated with the given switch. If the
+ // switch has no value or isn't present, this method returns
+ // the empty string.
+ std::wstring GetSwitchValue(const std::wstring& switch_string) const;
+
+ // Returns the number of "loose values" found in the command line.
+ // Loose values are arguments that aren't switches.
+ // (The program name is also excluded from the set of loose values.)
+ size_t GetLooseValueCount() const;
+
+ typedef std::vector<std::wstring>::const_iterator LooseValueIterator;
+
+ // Returns a const_iterator to the list of loose values.
+ LooseValueIterator GetLooseValuesBegin() const;
+
+ // Returns the end const_iterator for the list of loose values.
+ LooseValueIterator GetLooseValuesEnd() const;
+
+ // Simply returns the original command line string.
+ std::wstring command_line_string() const;
+
+ // Returns the program part of the command line string (the first item).
+ std::wstring program() const;
+
+ // An array containing the prefixes that identify an argument as
+ // a switch.
+ static const wchar_t* const kSwitchPrefixes[];
+
+ // The string that's used to separate switches from their values.
+ static const wchar_t kSwitchValueSeparator[];
+
+ // Appends the given switch string (preceded by a space and a switch
+ // prefix) to the given string.
+ static void AppendSwitch(std::wstring* command_line_string,
+ const std::wstring& switch_string);
+
+ // Appends the given switch string (preceded by a space and a switch
+ // prefix) to the given string, with the given value attached.
+ static void AppendSwitchWithValue(std::wstring* command_line_string,
+ const std::wstring& switch_string,
+ const std::wstring& value_string);
+
+ private:
+ class Data;
+
+ // True if we are responsible for deleting our |data_| pointer. In some cases
+ // we cache the result of parsing the command line and |data_|'s lifetime is
+ // managed by someone else (e.g., the |Singleton| class).
+ bool we_own_data_;
+
+ // A pointer to the parsed version of the command line.
+ Data* data_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CommandLine);
+};
+
+#endif // BASE_COMMAND_LINE_H__
diff --git a/base/command_line_unittest.cc b/base/command_line_unittest.cc
new file mode 100644
index 0000000..dbcf06f
--- /dev/null
+++ b/base/command_line_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class CommandLineTest : public testing::Test {
+ };
+};
+
+TEST(CommandLineTest, CommandLineConstructor) {
+ CommandLine cl(L"program --foo= -bAr /Spaetzel=pierogi /Baz flim "
+ L"--other-switches=\"--dog=canine --cat=feline\" "
+ L"-spaetzle=Crepe -=loosevalue flan "
+ L"--input-translation=\"45\"--output-rotation "
+ L"\"in the time of submarines...\"");
+
+ EXPECT_FALSE(cl.command_line_string().empty());
+ EXPECT_FALSE(cl.HasSwitch(L"cruller"));
+ EXPECT_FALSE(cl.HasSwitch(L"flim"));
+ EXPECT_FALSE(cl.HasSwitch(L"program"));
+ EXPECT_FALSE(cl.HasSwitch(L"dog"));
+ EXPECT_FALSE(cl.HasSwitch(L"cat"));
+ EXPECT_FALSE(cl.HasSwitch(L"output-rotation"));
+
+ EXPECT_EQ(L"program", cl.program());
+
+ EXPECT_TRUE(cl.HasSwitch(L"foo"));
+ EXPECT_TRUE(cl.HasSwitch(L"bar"));
+ EXPECT_TRUE(cl.HasSwitch(L"baz"));
+ EXPECT_TRUE(cl.HasSwitch(L"spaetzle"));
+ EXPECT_TRUE(cl.HasSwitch(L"SPAETZLE"));
+ EXPECT_TRUE(cl.HasSwitch(L"other-switches"));
+ EXPECT_TRUE(cl.HasSwitch(L"input-translation"));
+
+ EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle"));
+ EXPECT_EQ(L"", cl.GetSwitchValue(L"Foo"));
+ EXPECT_EQ(L"", cl.GetSwitchValue(L"bar"));
+ EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller"));
+ EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue(L"other-switches"));
+ EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation"));
+
+ EXPECT_EQ(3, cl.GetLooseValueCount());
+
+ CommandLine::LooseValueIterator iter = cl.GetLooseValuesBegin();
+ EXPECT_EQ(L"flim", *iter);
+ ++iter;
+ EXPECT_EQ(L"flan", *iter);
+ ++iter;
+ EXPECT_EQ(L"in the time of submarines...", *iter);
+ ++iter;
+ EXPECT_TRUE(iter == cl.GetLooseValuesEnd());
+}
+
+// These test the command line used to invoke the unit test.
+TEST(CommandLineTest, DefaultConstructor) {
+ CommandLine cl;
+ EXPECT_FALSE(cl.command_line_string().empty());
+ EXPECT_FALSE(cl.program().empty());
+}
+
+// Tests behavior with an empty input string.
+TEST(CommandLineTest, EmptyString) {
+ CommandLine cl(L"");
+ EXPECT_TRUE(cl.command_line_string().empty());
+ EXPECT_TRUE(cl.program().empty());
+ EXPECT_EQ(0, cl.GetLooseValueCount());
+}
+
+// Test static functions for appending switches to a command line.
+TEST(CommandLineTest, AppendSwitches) {
+ std::wstring cl_string = L"Program";
+ std::wstring switch1 = L"switch1";
+ std::wstring switch2 = L"switch2";
+ std::wstring value = L"value";
+ std::wstring switch3 = L"switch3";
+ std::wstring value3 = L"a value with spaces";
+ std::wstring switch4 = L"switch4";
+ std::wstring value4 = L"\"a value with quotes\"";
+
+ CommandLine::AppendSwitch(&cl_string, switch1);
+ CommandLine::AppendSwitchWithValue(&cl_string, switch2, value);
+ CommandLine::AppendSwitchWithValue(&cl_string, switch3, value3);
+ CommandLine::AppendSwitchWithValue(&cl_string, switch4, value4);
+ CommandLine cl(cl_string);
+
+ EXPECT_TRUE(cl.HasSwitch(switch1));
+ EXPECT_TRUE(cl.HasSwitch(switch2));
+ EXPECT_EQ(value, cl.GetSwitchValue(switch2));
+ EXPECT_TRUE(cl.HasSwitch(switch3));
+ EXPECT_EQ(value3, cl.GetSwitchValue(switch3));
+ EXPECT_TRUE(cl.HasSwitch(switch2));
+ EXPECT_EQ(value4.substr(1, value4.length() - 2), cl.GetSwitchValue(switch4));
+}
diff --git a/base/condition_variable.cc b/base/condition_variable.cc
new file mode 100644
index 0000000..0e4f7c8
--- /dev/null
+++ b/base/condition_variable.cc
@@ -0,0 +1,464 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/condition_variable.h"
+
+#include <stack>
+
+#include "base/lock.h"
+#include "base/logging.h"
+
+ConditionVariable::ConditionVariable(Lock* user_lock)
+ : user_lock_(*user_lock),
+ run_state_(RUNNING),
+ allocation_counter_(0),
+ recycling_list_size_(0) {
+ DCHECK(user_lock);
+}
+
+ConditionVariable::~ConditionVariable() {
+ AutoLock auto_lock(internal_lock_);
+ run_state_ = SHUTDOWN; // Prevent any more waiting.
+
+ DCHECK_EQ(recycling_list_size_, allocation_counter_);
+ if (recycling_list_size_ != allocation_counter_) { // Rare shutdown problem.
+ // There are threads of execution still in this->TimedWait() and yet the
+ // caller has instigated the destruction of this instance :-/.
+ // A common reason for such "overly hasty" destruction is that the caller
+ // was not willing to wait for all the threads to terminate. Such hasty
+ // actions are a violation of our usage contract, but we'll give the
+ // waiting thread(s) one last chance to exit gracefully (prior to our
+ // destruction).
+ // Note: waiting_list_ *might* be empty, but recycling is still pending.
+ AutoUnlock auto_unlock(internal_lock_);
+ Broadcast(); // Make sure all waiting threads have been signaled.
+ Sleep(10); // Give threads a chance to grab internal_lock_.
+ // All contained threads should be blocked on user_lock_ by now :-).
+ } // Reacquire internal_lock_.
+
+ DCHECK_EQ(recycling_list_size_, allocation_counter_);
+}
+
+// Wait() atomically releases the caller's lock as it starts to Wait, and then
+// reacquires it when it is signaled.
+void ConditionVariable::TimedWait(const TimeDelta& max_time) {
+ Event* waiting_event;
+ HANDLE handle;
+ {
+ AutoLock auto_lock(internal_lock_);
+ if (RUNNING != run_state_) return; // Destruction in progress.
+ waiting_event = GetEventForWaiting();
+ handle = waiting_event->handle();
+ DCHECK(handle);
+ } // Release internal_lock.
+
+ {
+ AutoUnlock unlock(user_lock_); // Release caller's lock
+ WaitForSingleObject(handle, static_cast<DWORD>(max_time.InMilliseconds()));
+ // Minimize spurious signal creation window by recycling asap.
+ AutoLock auto_lock(internal_lock_);
+ RecycleEvent(waiting_event);
+ // Release internal_lock_
+ } // Reacquire callers lock to depth at entry.
+}
+
+// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had
+// a cv_event internally allocated for them) before Broadcast() was called.
+void ConditionVariable::Broadcast() {
+ std::stack<HANDLE> handles; // See FAQ-question-10.
+ {
+ AutoLock auto_lock(internal_lock_);
+ if (waiting_list_.IsEmpty())
+ return;
+ while (!waiting_list_.IsEmpty())
+ // This is not a leak from waiting_list_. See FAQ-question 12.
+ handles.push(waiting_list_.PopBack()->handle());
+ } // Release internal_lock_.
+ while (!handles.empty()) {
+ SetEvent(handles.top());
+ handles.pop();
+ }
+}
+
+// Signal() will select one of the waiting threads, and signal it (signal its
+// cv_event). For better performance we signal the thread that went to sleep
+// most recently (LIFO). If we want fairness, then we wake the thread that has
+// been sleeping the longest (FIFO).
+void ConditionVariable::Signal() {
+ HANDLE handle;
+ {
+ AutoLock auto_lock(internal_lock_);
+ if (waiting_list_.IsEmpty())
+ return; // No one to signal.
+ // Only performance option should be used.
+ // This is not a leak from waiting_list. See FAQ-question 12.
+ handle = waiting_list_.PopBack()->handle(); // LIFO.
+ } // Release internal_lock_.
+ SetEvent(handle);
+}
+
+// GetEventForWaiting() provides a unique cv_event for any caller that needs to
+// wait. This means that (worst case) we may over time create as many cv_event
+// objects as there are threads simultaneously using this instance's Wait()
+// functionality.
+ConditionVariable::Event* ConditionVariable::GetEventForWaiting() {
+ // We hold internal_lock, courtesy of Wait().
+ Event* cv_event;
+ if (0 == recycling_list_size_) {
+ DCHECK(recycling_list_.IsEmpty());
+ cv_event = new Event();
+ cv_event->InitListElement();
+ allocation_counter_++;
+ // CHECK_NE is not defined in our codebase, so we have to use CHECK
+ CHECK(cv_event->handle());
+ } else {
+ cv_event = recycling_list_.PopFront();
+ recycling_list_size_--;
+ }
+ waiting_list_.PushBack(cv_event);
+ return cv_event;
+}
+
+// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and
+// recycles it for use in future Wait() calls for this or other threads.
+// Note that there is a tiny chance that the cv_event is still signaled when we
+// obtain it, and that can cause spurious signals (if/when we re-use the
+// cv_event), but such is quite rare (see FAQ-question-5).
+void ConditionVariable::RecycleEvent(Event* used_event) {
+ // We hold internal_lock, courtesy of Wait().
+ // If the cv_event timed out, then it is necessary to remove it from
+ // waiting_list_. If it was selected by Broadcast() or Signal(), then it is
+ // already gone.
+ used_event->Extract(); // Possibly redundant
+ recycling_list_.PushBack(used_event);
+ recycling_list_size_++;
+}
+//------------------------------------------------------------------------------
+// The next section provides the implementation for the private Event class.
+//------------------------------------------------------------------------------
+
+// Event provides a doubly-linked-list of events for use exclusively by the
+// ConditionVariable class.
+
+// This custom container was crafted because no simple combination of STL
+// classes appeared to support the functionality required. The specific
+// unusual requirement for a linked-list-class is support for the Extract()
+// method, which can remove an element from a list, potentially for insertion
+// into a second list. Most critically, the Extract() method is idempotent,
+// turning the indicated element into an extracted singleton whether it was
+// contained in a list or not. This functionality allows one (or more) of
+// threads to do the extraction. The iterator that identifies this extractable
+// element (in this case, a pointer to the list element) can be used after
+// arbitrary manipulation of the (possibly) enclosing list container. In
+// general, STL containers do not provide iterators that can be used across
+// modifications (insertions/extractions) of the enclosing containers, and
+// certainly don't provide iterators that can be used if the identified
+// element is *deleted* (removed) from the container.
+
+// It is possible to use multiple redundant containers, such as an STL list,
+// and an STL map, to achieve similar container semantics. This container has
+// only O(1) methods, while the corresponding (multiple) STL container approach
+// would have more complex O(log(N)) methods (yeah... N isn't that large).
+// Multiple containers also makes correctness more difficult to assert, as
+// data is redundantly stored and maintained, which is generally evil.
+
+ConditionVariable::Event::Event() : handle_(0) {
+ next_ = prev_ = this; // Self referencing circular.
+}
+
+ConditionVariable::Event::~Event() {
+ if (0 == handle_) {
+ // This is the list holder
+ while (!IsEmpty()) {
+ Event* cv_event = PopFront();
+ DCHECK(cv_event->ValidateAsItem());
+ delete cv_event;
+ }
+ }
+ DCHECK(IsSingleton());
+ if (0 != handle_) {
+ int ret_val = CloseHandle(handle_);
+ DCHECK(ret_val);
+ }
+}
+
+// Change a container instance permanently into an element of a list.
+void ConditionVariable::Event::InitListElement() {
+ DCHECK(!handle_);
+ handle_ = CreateEvent(NULL, false, false, NULL);
+ CHECK(handle_);
+}
+
+// Methods for use on lists.
+bool ConditionVariable::Event::IsEmpty() const {
+ DCHECK(ValidateAsList());
+ return IsSingleton();
+}
+
+void ConditionVariable::Event::PushBack(Event* other) {
+ DCHECK(ValidateAsList());
+ DCHECK(other->ValidateAsItem());
+ DCHECK(other->IsSingleton());
+ // Prepare other for insertion.
+ other->prev_ = prev_;
+ other->next_ = this;
+ // Cut into list.
+ prev_->next_ = other;
+ prev_ = other;
+ DCHECK(ValidateAsDistinct(other));
+}
+
+ConditionVariable::Event* ConditionVariable::Event::PopFront() {
+ DCHECK(ValidateAsList());
+ DCHECK(!IsSingleton());
+ return next_->Extract();
+}
+
+ConditionVariable::Event* ConditionVariable::Event::PopBack() {
+ DCHECK(ValidateAsList());
+ DCHECK(!IsSingleton());
+ return prev_->Extract();
+}
+
+// Methods for use on list elements.
+// Accessor method.
+HANDLE ConditionVariable::Event::handle() const {
+ DCHECK(ValidateAsItem());
+ return handle_;
+}
+
+// Pull an element from a list (if it's in one).
+ConditionVariable::Event* ConditionVariable::Event::Extract() {
+ DCHECK(ValidateAsItem());
+ if (!IsSingleton()) {
+ // Stitch neighbors together.
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ // Make extractee into a singleton.
+ prev_ = next_ = this;
+ }
+ DCHECK(IsSingleton());
+ return this;
+}
+
+// Method for use on a list element or on a list.
+bool ConditionVariable::Event::IsSingleton() const {
+ DCHECK(ValidateLinks());
+ return next_ == this;
+}
+
+// Provide pre/post conditions to validate correct manipulations.
+bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const {
+ return ValidateLinks() && other->ValidateLinks() && (this != other);
+}
+
+bool ConditionVariable::Event::ValidateAsItem() const {
+ return (0 != handle_) && ValidateLinks();
+}
+
+bool ConditionVariable::Event::ValidateAsList() const {
+ return (0 == handle_) && ValidateLinks();
+}
+
+bool ConditionVariable::Event::ValidateLinks() const {
+ // Make sure both of our neighbors have links that point back to us.
+ // We don't do the O(n) check and traverse the whole loop, and instead only
+ // do a local check to (and returning from) our immediate neighbors.
+ return (next_->prev_ == this) && (prev_->next_ == this);
+}
+
+
+/*
+FAQ On subtle implementation details:
+
+1) What makes this problem subtle? Please take a look at "Strategies
+for Implementing POSIX Condition Variables on Win32" by Douglas
+C. Schmidt and Irfan Pyarali.
+http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes
+discussions of numerous flawed strategies for implementing this
+functionality. I'm not convinced that even the final proposed
+implementation has semantics that are as nice as this implementation
+(especially with regard to Broadcast() and the impact on threads that
+try to Wait() after a Broadcast() has been called, but before all the
+original waiting threads have been signaled).
+
+2) Why can't you use a single wait_event for all threads that call
+Wait()? See FAQ-question-1, or consider the following: If a single
+event were used, then numerous threads calling Wait() could release
+their cs locks, and be preempted just before calling
+WaitForSingleObject(). If a call to Broadcast() was then presented on
+a second thread, it would be impossible to actually signal all
+waiting(?) threads. Some number of SetEvent() calls *could* be made,
+but there could be no guarantee that those led to to more than one
+signaled thread (SetEvent()'s may be discarded after the first!), and
+there could be no guarantee that the SetEvent() calls didn't just
+awaken "other" threads that hadn't even started waiting yet (oops).
+Without any limit on the number of requisite SetEvent() calls, the
+system would be forced to do many such calls, allowing many new waits
+to receive spurious signals.
+
+3) How does this implementation cause spurious signal events? The
+cause in this implementation involves a race between a signal via
+time-out and a signal via Signal() or Broadcast(). The series of
+actions leading to this are:
+
+a) Timer fires, and a waiting thread exits the line of code:
+
+ WaitForSingleObject(waiting_event, max_time.InMilliseconds());
+
+b) That thread (in (a)) is randomly pre-empted after the above line,
+leaving the waiting_event reset (unsignaled) and still in the
+waiting_list_.
+
+c) A call to Signal() (or Broadcast()) on a second thread proceeds, and
+selects the waiting cv_event (identified in step (b)) as the event to revive
+via a call to SetEvent().
+
+d) The Signal() method (step c) calls SetEvent() on waiting_event (step b).
+
+e) The waiting cv_event (step b) is now signaled, but no thread is
+waiting on it.
+
+f) When that waiting_event (step b) is reused, it will immediately
+be signaled (spuriously).
+
+
+4) Why do you recycle events, and cause spurious signals? First off,
+the spurious events are very rare. They can only (I think) appear
+when the race described in FAQ-question-3 takes place. This should be
+very rare. Most(?) uses will involve only timer expiration, or only
+Signal/Broadcast() actions. When both are used, it will be rare that
+the race will appear, and it would require MANY Wait() and signaling
+activities. If this implementation did not recycle events, then it
+would have to create and destroy events for every call to Wait().
+That allocation/deallocation and associated construction/destruction
+would be costly (per wait), and would only be a rare benefit (when the
+race was "lost" and a spurious signal took place). That would be bad
+(IMO) optimization trade-off. Finally, such spurious events are
+allowed by the specification of condition variables (such as
+implemented in Vista), and hence it is better if any user accommodates
+such spurious events (see usage note in condition_variable.h).
+
+5) Why don't you reset events when you are about to recycle them, or
+about to reuse them, so that the spurious signals don't take place?
+The thread described in FAQ-question-3 step c may be pre-empted for an
+arbitrary length of time before proceeding to step d. As a result,
+the wait_event may actually be re-used *before* step (e) is reached.
+As a result, calling reset would not help significantly.
+
+6) How is it that the callers lock is released atomically with the
+entry into a wait state? We commit to the wait activity when we
+allocate the wait_event for use in a given call to Wait(). This
+allocation takes place before the caller's lock is released (and
+actually before our internal_lock_ is released). That allocation is
+the defining moment when "the wait state has been entered," as that
+thread *can* now be signaled by a call to Broadcast() or Signal().
+Hence we actually "commit to wait" before releasing the lock, making
+the pair effectively atomic.
+
+8) Why do you need to lock your data structures during waiting, as the
+caller is already in possession of a lock? We need to Acquire() and
+Release() our internal lock during Signal() and Broadcast(). If we tried
+to use a callers lock for this purpose, we might conflict with their
+external use of the lock. For example, the caller may use to consistently
+hold a lock on one thread while calling Signal() on another, and that would
+block Signal().
+
+9) Couldn't a more efficient implementation be provided if you
+preclude using more than one external lock in conjunction with a
+single ConditionVariable instance? Yes, at least it could be viewed
+as a simpler API (since you don't have to reiterate the lock argument
+in each Wait() call). One of the constructors now takes a specific
+lock as an argument, and a there are corresponding Wait() calls that
+don't specify a lock now. It turns that the resulting implmentation
+can't be made more efficient, as the internal lock needs to be used by
+Signal() and Broadcast(), to access internal data structures. As a
+result, I was not able to utilize the user supplied lock (which is
+being used by the user elsewhere presumably) to protect the private
+member access.
+
+9) Since you have a second lock, how can be be sure that there is no
+possible deadlock scenario? Our internal_lock_ is always the last
+lock acquired, and the first one released, and hence a deadlock (due
+to critical section problems) is impossible as a consequence of our
+lock.
+
+10) When doing a Broadcast(), why did you copy all the events into
+an STL queue, rather than making a linked-loop, and iterating over it?
+The iterating during Broadcast() is done so outside the protection
+of the internal lock. As a result, other threads, such as the thread
+wherein a related event is waiting, could asynchronously manipulate
+the links around a cv_event. As a result, the link structure cannot
+be used outside a lock. Broadcast() could iterate over waiting
+events by cycling in-and-out of the protection of the internal_lock,
+but that appears more expensive than copying the list into an STL
+stack.
+
+11) Why did the lock.h file need to be modified so much for this
+change? Central to a Condition Variable is the atomic release of a
+lock during a Wait(). This places Wait() functionality exactly
+mid-way between the two classes, Lock and Condition Variable. Given
+that there can be nested Acquire()'s of locks, and Wait() had to
+Release() completely a held lock, it was necessary to augment the Lock
+class with a recursion counter. Even more subtle is the fact that the
+recursion counter (in a Lock) must be protected, as many threads can
+access it asynchronously. As a positive fallout of this, there are
+now some DCHECKS to be sure no one Release()s a Lock more than they
+Acquire()ed it, and there is ifdef'ed functionality that can detect
+nested locks (legal under windows, but not under Posix).
+
+12) Why is it that the cv_events removed from list in Broadcast() and Signal()
+are not leaked? How are they recovered?? The cv_events that appear to leak are
+taken from the waiting_list_. For each element in that list, there is currently
+a thread in or around the WaitForSingleObject() call of Wait(), and those
+threads have references to these otherwise leaked events. They are passed as
+arguments to be recycled just aftre returning from WaitForSingleObject().
+
+13) Why did you use a custom container class (the linked list), when STL has
+perfectly good containers, such as an STL list? The STL list, as with any
+container, does not guarantee the utility of an iterator across manipulation
+(such as insertions and deletions) of the underlying container. The custom
+double-linked-list container provided that assurance. I don't believe any
+combination of STL containers provided the services that were needed at the same
+O(1) efficiency as the custom linked list. The unusual requirement
+for the container class is that a reference to an item within a container (an
+iterator) needed to be maintained across an arbitrary manipulation of the
+container. This requirement exposes itself in the Wait() method, where a
+waiting_event must be selected prior to the WaitForSingleObject(), and then it
+must be used as part of recycling to remove the related instance from the
+waiting_list. A hash table (STL map) could be used, but I was embarrased to
+use a complex and relatively low efficiency container when a doubly linked list
+provided O(1) performance in all required operations. Since other operations
+to provide performance-and/or-fairness required queue (FIFO) and list (LIFO)
+containers, I would also have needed to use an STL list/queue as well as an STL
+map. In the end I decided it would be "fun" to just do it right, and I
+put so many assertions (DCHECKs) into the container class that it is trivial to
+code review and validate its correctness.
+
+*/
diff --git a/base/condition_variable.h b/base/condition_variable.h
new file mode 100644
index 0000000..bdb9a19
--- /dev/null
+++ b/base/condition_variable.h
@@ -0,0 +1,195 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// ConditionVariable is a reasonable attempt at simulating
+// the newer Posix and Vista-only construct for condition variable
+// synchronization. This functionality is very helpful for having several
+// threads wait for an event, as is common with a thread pool
+// managed by a master. The meaning of such an event in the
+// (worker) thread pool scenario is that additional tasks are
+// now available for processing. It is used in Chrome in the
+// DNS prefetching system to notify worker threads that a queue
+// now has items (tasks) which need to be tended to.
+// A related use would have a pool manager waiting on a
+// ConditionVariable, waiting for a thread in the pool to announce
+// (signal) that there is now more room in a (bounded size) communications
+// queue for the manager to deposit tasks, or, as a second example, that
+// the queue of tasks is completely empty and all workers are waiting.
+
+// USAGE NOTE 1: spurious signal events are possible with this and
+// most implementations of condition variables. As a result, be
+// *sure* to retest your condition before proceeding. The following
+// is a good example of doing this correctly:
+
+// while (!work_to_be_done()) Wait(...);
+
+// In contrast do NOT do the following:
+
+// if (!work_to_be_done()) Wait(...); // Don't do this.
+
+// Especially avoid the above if you are relying on some other thread only
+// issuing a signal up *if* there is work-to-do. There can/will
+// be spurious signals. Recheck state on waiting thread before
+// assuming the signal was intentional. Caveat caller ;-).
+
+// USAGE NOTE 2: Broadcast() frees up all waiting threads at once,
+// which leads to contention for the locks they all held when they
+// called Wait(). This results in POOR performance. A much better
+// approach to getting a lot of threads out of Wait() is to have each
+// thread (upon exiting Wait()) call Signal() to free up another
+// Wait'ing thread. Look at condition_variable_unittest.cc for
+// both examples.
+
+// Broadcast() can be used nicely during teardown, as it gets the job
+// done, and leaves no sleeping threads... and performance is less
+// critical at that point.
+
+// The semantics of Broadcast() are carefully crafted so that *all*
+// threads that were waiting when the request was made will indeed
+// get signaled. Some implementations mess up, and don't signal them
+// all, while others allow the wait to be effectively turned off (for
+// for a while while waiting threads come around). This implementation
+// appears correct, as it will not "lose" any signals, and will guarantee
+// that all threads get signaled by Broadcast().
+
+// This implementation offers support for "performance" in its selection of
+// which thread to revive. Performance, in direct contrast with "fairness,"
+// assures that the thread that most recently began to Wait() is selected by
+// Signal to revive. Fairness would (if publicly supported) assure that the
+// thread that has Wait()ed the longest is selected. The default policy
+// may improve performance, as the selected thread may have a greater chance of
+// having some of its stack data in various CPU caches.
+
+// For a discussion of the many very subtle implementation details, see the FAQ
+// at the end of condition_variable.cc.
+
+#ifndef BASE_CONDITION_VARIABLE_H__
+#define BASE_CONDITION_VARIABLE_H__
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/time.h"
+
+class Lock;
+
+class ConditionVariable {
+ public:
+ // Construct a cv for use with ONLY one user lock.
+ explicit ConditionVariable(Lock* user_lock);
+
+ ~ConditionVariable();
+
+ // Wait() releases the caller's critical section atomically as it starts to
+ // sleep, and the reacquires it when it is signaled.
+ void TimedWait(const TimeDelta& max_time);
+ void Wait() {
+ // Default to "wait forever" timing, which means have to get a Signal()
+ // or Broadcast() to come out of this wait state.
+ TimedWait(TimeDelta::FromMilliseconds(INFINITE));
+ }
+
+ // Broadcast() revives all waiting threads.
+ void Broadcast();
+ // Signal() revives one waiting thread.
+ void Signal();
+
+ private:
+ // Define Event class that is used to form circularly linked lists.
+ // The list container is an element with NULL as its handle_ value.
+ // The actual list elements have a non-zero handle_ value.
+ // All calls to methods MUST be done under protection of a lock so that links
+ // can be validated. Without the lock, some links might asynchronously
+ // change, and the assertions would fail (as would list change operations).
+ class Event {
+ public:
+ // Default constructor with no arguments creates a list container.
+ Event();
+ ~Event();
+
+ // InitListElement transitions an instance from a container, to an element.
+ void InitListElement();
+
+ // Methods for use on lists.
+ bool IsEmpty() const;
+ void PushBack(Event* other);
+ Event* PopFront();
+ Event* PopBack();
+
+ // Methods for use on list elements.
+ // Accessor method.
+ HANDLE handle() const;
+ // Pull an element from a list (if it's in one).
+ Event* Extract();
+
+ // Method for use on a list element or on a list.
+ bool IsSingleton() const;
+
+ private:
+ // Provide pre/post conditions to validate correct manipulations.
+ bool ValidateAsDistinct(Event* other) const;
+ bool ValidateAsItem() const;
+ bool ValidateAsList() const;
+ bool ValidateLinks() const;
+
+ HANDLE handle_;
+ Event* next_;
+ Event* prev_;
+ DISALLOW_EVIL_CONSTRUCTORS(Event);
+ };
+
+ // Note that RUNNING is an unlikely number to have in RAM by accident.
+ // This helps with defensive destructor coding in the face of user error.
+ enum RunState { SHUTDOWN = 0, RUNNING = 64213 };
+
+ // Internal implementation methods supporting Wait().
+ Event* GetEventForWaiting();
+ void RecycleEvent(Event* used_event);
+
+ RunState run_state_;
+
+ // Private critical section for access to member data.
+ Lock internal_lock_;
+ // Lock that is acquired before calling Wait().
+ Lock& user_lock_;
+
+ // Events that threads are blocked on.
+ Event waiting_list_;
+
+ // Free list for old events.
+ Event recycling_list_;
+ int recycling_list_size_;
+
+ // The number of allocated, but not yet deleted events.
+ int allocation_counter_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ConditionVariable);
+};
+
+#endif // BASE_CONDITION_VARIABLE_H__
diff --git a/base/condition_variable_test.cc b/base/condition_variable_test.cc
new file mode 100644
index 0000000..35efa5f
--- /dev/null
+++ b/base/condition_variable_test.cc
@@ -0,0 +1,707 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Multi-threaded tests of ConditionVariable class.
+
+#include <time.h>
+#include <algorithm>
+#include <vector>
+
+#include "base/check_handler.h"
+#include "base/condition_variable.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/spin_wait.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+//------------------------------------------------------------------------------
+// Define our test class, with several common variables.
+//------------------------------------------------------------------------------
+
+class ConditionVariableTest : public testing::Test {
+ public:
+ const TimeDelta kZeroMs;
+ const TimeDelta kTenMs;
+ const TimeDelta kThirtyMs;
+ const TimeDelta kFortyFiveMs;
+ const TimeDelta kSixtyMs;
+ const TimeDelta kOneHundredMs;
+
+ explicit ConditionVariableTest()
+ : kZeroMs(TimeDelta::FromMilliseconds(0)),
+ kTenMs(TimeDelta::FromMilliseconds(10)),
+ kThirtyMs(TimeDelta::FromMilliseconds(30)),
+ kFortyFiveMs(TimeDelta::FromMilliseconds(45)),
+ kSixtyMs(TimeDelta::FromMilliseconds(60)),
+ kOneHundredMs(TimeDelta::FromMilliseconds(100)) {
+ }
+};
+
+//------------------------------------------------------------------------------
+// Define a class that will control activities an several multi-threaded tests.
+// The general structure of multi-threaded tests is that a test case will
+// construct an instance of a WorkQueue. The WorkQueue will spin up some
+// threads and control them thoughout their lifetime, as well as maintaining
+// a central respository of the work thread's activity. Finally, the WorkQueue
+// will command the the worker threads to terminate. At that point, the test
+// cases will validate that the WorkQueue has records showing that the desired
+// activities were performed.
+//------------------------------------------------------------------------------
+// Forward declare the WorkerProcess task
+static DWORD WINAPI WorkerProcess(void* p);
+
+// Callers are responsible for synchronizing access to the following class.
+// The WorkQueue::lock_, as accessed via WorkQueue::lock(), should be used for
+// all synchronized access.
+class WorkQueue {
+ public:
+ explicit WorkQueue(int thread_count);
+ ~WorkQueue();
+
+ //----------------------------------------------------------------------------
+ // Worker threads only call the following methods.
+ // They should use the lock to get exclusive access.
+ int GetThreadId(); // Get an ID assigned to a thread..
+ bool EveryIdWasAllocated() const; // Indicates that all IDs were handed out.
+ TimeDelta GetAnAssignment(int thread_id); // Get a work task duration.
+ void WorkIsCompleted(int thread_id);
+
+ int task_count() const;
+ bool allow_help_requests() const; // Workers can signal more workers.
+ bool shutdown() const; // Check if shutdown has been requested.
+ int shutdown_task_count() const;
+
+ void thread_shutting_down();
+ Lock* lock();
+
+ ConditionVariable* work_is_available();
+ ConditionVariable* all_threads_have_ids();
+ ConditionVariable* no_more_tasks();
+
+ //----------------------------------------------------------------------------
+ // The rest of the methods are for use by the controlling master thread (the
+ // test case code).
+ void ResetHistory();
+ int GetMinCompletionsByWorkerThread() const;
+ int GetMaxCompletionsByWorkerThread() const;
+ int GetNumThreadsTakingAssignments() const;
+ int GetNumThreadsCompletingTasks() const;
+ int GetNumberOfCompletedTasks() const;
+
+ void SetWorkTime(TimeDelta delay);
+ void SetTaskCount(int count);
+ void SetAllowHelp(bool allow);
+
+ void SetShutdown();
+
+ private:
+ // Both worker threads and controller use the following to synchronize.
+ Lock lock_;
+ ConditionVariable work_is_available_; // To tell threads there is work.
+
+ // Conditions to notify the controlling process (if it is interested).
+ ConditionVariable all_threads_have_ids_; // All threads are running.
+ ConditionVariable no_more_tasks_; // Task count is zero.
+
+ const int thread_count_;
+ scoped_array<HANDLE> handles_;
+ std::vector<int> assignment_history_; // Number of assignment per worker.
+ std::vector<int> completion_history_; // Number of completions per worker.
+ int thread_started_counter_; // Used to issue unique id to workers.
+ int shutdown_task_count_; // Number of tasks told to shutdown
+ int task_count_; // Number of assignment tasks waiting to be processed.
+ TimeDelta worker_delay_; // Time each task takes to complete.
+ bool allow_help_requests_; // Workers can signal more workers.
+ bool shutdown_; // Set when threads need to terminate.
+};
+
+//------------------------------------------------------------------------------
+// Define the standard worker task. Several tests will spin out many of these
+// threads.
+//------------------------------------------------------------------------------
+
+// The multithread tests involve several threads with a task to perform as
+// directed by an instance of the class WorkQueue.
+// The task is to:
+// a) Check to see if there are more tasks (there is a task counter).
+// a1) Wait on condition variable if there are no tasks currently.
+// b) Call a function to see what should be done.
+// c) Do some computation based on the number of milliseconds returned in (b).
+// d) go back to (a).
+
+// WorkerProcess() implements the above task for all threads.
+// It calls the controlling object to tell the creator about progress, and to
+// ask about tasks.
+static DWORD WINAPI WorkerProcess(void* p) {
+ int thread_id;
+ class WorkQueue* queue = reinterpret_cast<WorkQueue*>(p);
+ {
+ AutoLock auto_lock(*queue->lock());
+ thread_id = queue->GetThreadId();
+ if (queue->EveryIdWasAllocated())
+ queue->all_threads_have_ids()->Signal(); // Tell creator we're ready.
+ }
+
+ Lock private_lock; // Used to waste time on "our work".
+ while (1) { // This is the main consumer loop.
+ TimeDelta work_time;
+ bool could_use_help;
+ {
+ AutoLock auto_lock(*queue->lock());
+ while (0 == queue->task_count() && !queue->shutdown()) {
+ queue->work_is_available()->Wait();
+ }
+ if (queue->shutdown()) {
+ // Ack the notification of a shutdown message back to the controller.
+ queue->thread_shutting_down();
+ return 0; // Terminate.
+ }
+ // Get our task duration from the queue.
+ work_time = queue->GetAnAssignment(thread_id);
+ could_use_help = (queue->task_count() > 0) &&
+ queue->allow_help_requests();
+ } // Release lock
+
+ // Do work (outside of locked region.
+ if (could_use_help)
+ queue->work_is_available()->Signal(); // Get help from other threads.
+
+ if (work_time > TimeDelta::FromMilliseconds(0)) {
+ // We could just sleep(), but we'll instead further exercise the
+ // condition variable class, and do a timed wait.
+ AutoLock auto_lock(private_lock);
+ ConditionVariable private_cv(&private_lock);
+ private_cv.TimedWait(work_time); // Unsynchronized waiting.
+ }
+
+ {
+ AutoLock auto_lock(*queue->lock());
+ // Send notification that we completed our "work."
+ queue->WorkIsCompleted(thread_id);
+ }
+ }
+}
+//------------------------------------------------------------------------------
+// The next section contains the actual tests.
+//------------------------------------------------------------------------------
+
+TEST_F(ConditionVariableTest, StartupShutdownTest) {
+ Lock lock;
+
+ // First try trivial startup/shutdown.
+ {
+ ConditionVariable cv1(&lock);
+ } // Call for cv1 destruction.
+
+ // Exercise with at least a few waits.
+ ConditionVariable cv(&lock);
+
+ lock.Acquire();
+ cv.TimedWait(kTenMs); // Wait for 10 ms.
+ cv.TimedWait(kTenMs); // Wait for 10 ms.
+ lock.Release();
+
+ lock.Acquire();
+ cv.TimedWait(kTenMs); // Wait for 10 ms.
+ cv.TimedWait(kTenMs); // Wait for 10 ms.
+ cv.TimedWait(kTenMs); // Wait for 10 ms.
+ lock.Release();
+} // Call for cv destruction.
+
+TEST_F(ConditionVariableTest, LockedExpressionTest) {
+ int i = 0;
+ Lock lock;
+
+ // Old LOCKED_EXPRESSION macro caused syntax errors here.
+ // ... yes... compiler will optimize this example.
+ // Syntax error is what I'm after precluding.
+ if (0)
+ LOCKED_EXPRESSION(lock, i = 1);
+ else
+ LOCKED_EXPRESSION(lock, i = 2);
+
+ EXPECT_EQ(2, i);
+}
+
+TEST_F(ConditionVariableTest, TimeoutTest) {
+ Lock lock;
+ ConditionVariable cv(&lock);
+ lock.Acquire();
+
+ TimeTicks start = TimeTicks::Now();
+ const TimeDelta WAIT_TIME = TimeDelta::FromMilliseconds(300);
+ // Allow for clocking rate granularity.
+ const TimeDelta FUDGE_TIME = TimeDelta::FromMilliseconds(50);
+
+ cv.TimedWait(WAIT_TIME + FUDGE_TIME);
+ TimeDelta duration = TimeTicks::Now() - start;
+ // We can't use EXPECT_GE here as the TimeDelta class does not support the
+ // required stream conversion.
+ EXPECT_TRUE(duration >= WAIT_TIME);
+
+ lock.Release();
+}
+
+TEST_F(ConditionVariableTest, MultiThreadConsumerTest) {
+ const int kThreadCount = 10;
+ WorkQueue queue(kThreadCount); // Start the threads.
+
+ Lock private_lock; // Used locally for master to wait.
+ AutoLock private_held_lock(private_lock);
+ ConditionVariable private_cv(&private_lock);
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ while (!queue.EveryIdWasAllocated())
+ queue.all_threads_have_ids()->Wait();
+ }
+
+ // Wait a bit more to allow threads to reach their wait state.
+ private_cv.TimedWait(kTenMs);
+
+ {
+ // Since we have no tasks, all threads should be waiting by now.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make one worker do 3 30ms tasks.
+ queue.ResetHistory();
+ queue.SetTaskCount(3);
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(false);
+ }
+ queue.work_is_available()->Signal(); // Start up one thread.
+ // Wait to allow solo worker insufficient time to get done.
+ private_cv.TimedWait(kFortyFiveMs); // Should take about 90 ms.
+
+ {
+ // Check that all work HASN'T completed yet.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
+ EXPECT_GT(2, queue.task_count()); // 2 should have started.
+ EXPECT_GT(3, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(1, queue.GetNumberOfCompletedTasks());
+ }
+ // Wait to allow solo workers to get done.
+ private_cv.TimedWait(kSixtyMs); // Should take about 45ms more.
+
+ {
+ // Check that all work was done by one thread id.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(3, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make each task include getting help from another worker.
+ queue.ResetHistory();
+ queue.SetTaskCount(3);
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true);
+ }
+ queue.work_is_available()->Signal(); // But each worker can signal another.
+ // Wait to allow the 3 workers to get done.
+ private_cv.TimedWait(kFortyFiveMs); // Should take about 30 ms.
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+ // Try to ask all workers to help, and only a few will do the work.
+ queue.ResetHistory();
+ queue.SetTaskCount(3);
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(false);
+ }
+ queue.work_is_available()->Broadcast(); // Make them all try.
+ // Wait to allow the 3 workers to get done.
+ private_cv.TimedWait(kFortyFiveMs);
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make each task get help from another worker.
+ queue.ResetHistory();
+ queue.SetTaskCount(3);
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true); // Allow (unnecessary) help requests.
+ }
+ queue.work_is_available()->Broadcast(); // We already signal all threads.
+ // Wait to allow the 3 workers to get done.
+ private_cv.TimedWait(kOneHundredMs);
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(3, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make each task get help from another worker.
+ queue.ResetHistory();
+ queue.SetTaskCount(20);
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true);
+ }
+ queue.work_is_available()->Signal(); // But each worker can signal another.
+ // Wait to allow the 10 workers to get done.
+ private_cv.TimedWait(kOneHundredMs); // Should take about 60 ms.
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
+
+ // Same as last test, but with Broadcast().
+ queue.ResetHistory();
+ queue.SetTaskCount(20); // 2 tasks per process.
+ queue.SetWorkTime(kThirtyMs);
+ queue.SetAllowHelp(true);
+ }
+ queue.work_is_available()->Broadcast();
+ // Wait to allow the 10 workers to get done.
+ private_cv.TimedWait(kOneHundredMs); // Should take about 60 ms.
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(20, queue.GetNumberOfCompletedTasks());
+
+ queue.SetShutdown();
+ }
+ queue.work_is_available()->Broadcast(); // Force check for shutdown.
+
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+ queue.shutdown_task_count() == kThreadCount);
+ Sleep(10); // Be sure they're all shutdown.
+}
+
+TEST_F(ConditionVariableTest, LargeFastTaskTest) {
+ const int kThreadCount = 200;
+ WorkQueue queue(kThreadCount); // Start the threads.
+
+ Lock private_lock; // Used locally for master to wait.
+ AutoLock private_held_lock(private_lock);
+ ConditionVariable private_cv(&private_lock);
+
+ {
+ AutoLock auto_lock(*queue.lock());
+ while (!queue.EveryIdWasAllocated())
+ queue.all_threads_have_ids()->Wait();
+ }
+
+ // Wait a bit more to allow threads to reach their wait state.
+ private_cv.TimedWait(kThirtyMs);
+
+ {
+ // Since we have no tasks, all threads should be waiting by now.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread());
+ EXPECT_EQ(0, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make all workers do (an average of) 20 tasks.
+ queue.ResetHistory();
+ queue.SetTaskCount(20 * kThreadCount);
+ queue.SetWorkTime(kFortyFiveMs);
+ queue.SetAllowHelp(false);
+ }
+ queue.work_is_available()->Broadcast(); // Start up all threads.
+ // Wait until we've handed out all tasks.
+ {
+ AutoLock auto_lock(*queue.lock());
+ while (queue.task_count() != 0)
+ queue.no_more_tasks()->Wait();
+ }
+
+ // Wait till the last of the tasks complete.
+ // Don't bother to use locks: We may not get info in time... but we'll see it
+ // eventually.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+ 20 * kThreadCount ==
+ queue.GetNumberOfCompletedTasks());
+
+ {
+ // With Broadcast(), every thread should have participated.
+ // but with racing.. they may not all have done equal numbers of tasks.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_LE(20, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(20 * kThreadCount, queue.GetNumberOfCompletedTasks());
+
+ // Set up to make all workers do (an average of) 4 tasks.
+ queue.ResetHistory();
+ queue.SetTaskCount(kThreadCount * 4);
+ queue.SetWorkTime(kFortyFiveMs);
+ queue.SetAllowHelp(true); // Might outperform Broadcast().
+ }
+ queue.work_is_available()->Signal(); // Start up one thread.
+
+ // Wait until we've handed out all tasks
+ {
+ AutoLock auto_lock(*queue.lock());
+ while (queue.task_count() != 0)
+ queue.no_more_tasks()->Wait();
+ }
+
+ // Wait till the last of the tasks complete.
+ // Don't bother to use locks: We may not get info in time... but we'll see it
+ // eventually.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+ 4 * kThreadCount ==
+ queue.GetNumberOfCompletedTasks());
+
+ {
+ // With Signal(), every thread should have participated.
+ // but with racing.. they may not all have done four tasks.
+ AutoLock auto_lock(*queue.lock());
+ EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments());
+ EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks());
+ EXPECT_EQ(0, queue.task_count());
+ EXPECT_LE(4, queue.GetMaxCompletionsByWorkerThread());
+ EXPECT_EQ(4 * kThreadCount, queue.GetNumberOfCompletedTasks());
+
+ queue.SetShutdown();
+ }
+ queue.work_is_available()->Broadcast(); // Force check for shutdown.
+
+ // Wait for shutdows to complete.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
+ queue.shutdown_task_count() == kThreadCount);
+ Sleep(10); // Be sure they're all shutdown.
+}
+
+//------------------------------------------------------------------------------
+// Finally we provide the implementation for the methods in the WorkQueue class.
+//------------------------------------------------------------------------------
+
+WorkQueue::WorkQueue(int thread_count)
+ : lock_(),
+ work_is_available_(&lock_),
+ all_threads_have_ids_(&lock_),
+ no_more_tasks_(&lock_),
+ thread_count_(thread_count),
+ handles_(new HANDLE[thread_count]),
+ assignment_history_(thread_count),
+ completion_history_(thread_count),
+ thread_started_counter_(0),
+ shutdown_task_count_(0),
+ task_count_(0),
+ allow_help_requests_(false),
+ shutdown_(false) {
+ EXPECT_GE(thread_count_, 1);
+ ResetHistory();
+ SetTaskCount(0);
+ SetWorkTime(TimeDelta::FromMilliseconds(30));
+
+ for (int i = 0; i < thread_count_; ++i) {
+ handles_[i] = CreateThread(NULL, // security.
+ 0, // <64K stack size.
+ WorkerProcess, // Static function.
+ reinterpret_cast<void*>(this),
+ 0, // Create running process.
+ NULL); // OS version of thread id.
+ EXPECT_NE(reinterpret_cast<void*>(NULL), handles_[i]);
+ }
+}
+
+WorkQueue::~WorkQueue() {
+ {
+ AutoLock auto_lock(lock_);
+ SetShutdown();
+ }
+ work_is_available_.Broadcast(); // Tell them all to terminate.
+ DWORD result = WaitForMultipleObjects(
+ thread_count_,
+ &handles_[0],
+ true, // Wait for all
+ 10000); // Ten seconds max.
+
+ for (int i = 0; i < thread_count_; ++i) {
+ int ret_value = CloseHandle(handles_[i]);
+ CHECK(ret_value);
+ handles_[i] = NULL;
+ }
+}
+
+int WorkQueue::GetThreadId() {
+ DCHECK(!EveryIdWasAllocated());
+ return thread_started_counter_++; // Give out Unique IDs.
+}
+
+bool WorkQueue::EveryIdWasAllocated() const {
+ return thread_count_ == thread_started_counter_;
+}
+
+TimeDelta WorkQueue::GetAnAssignment(int thread_id) {
+ DCHECK_LT(0, task_count_);
+ assignment_history_[thread_id]++;
+ if (0 == --task_count_) {
+ no_more_tasks_.Signal();
+ }
+ return worker_delay_;
+}
+
+void WorkQueue::WorkIsCompleted(int thread_id) {
+ completion_history_[thread_id]++;
+}
+
+int WorkQueue::task_count() const {
+ return task_count_;
+}
+
+bool WorkQueue::allow_help_requests() const {
+ return allow_help_requests_;
+}
+
+bool WorkQueue::shutdown() const {
+ return shutdown_;
+}
+
+int WorkQueue::shutdown_task_count() const {
+ return shutdown_task_count_;
+}
+
+void WorkQueue::thread_shutting_down() {
+ shutdown_task_count_++;
+}
+
+Lock* WorkQueue::lock() {
+ return &lock_;
+}
+
+ConditionVariable* WorkQueue::work_is_available() {
+ return &work_is_available_;
+}
+
+ConditionVariable* WorkQueue::all_threads_have_ids() {
+ return &all_threads_have_ids_;
+}
+
+ConditionVariable* WorkQueue::no_more_tasks() {
+ return &no_more_tasks_;
+}
+
+void WorkQueue::ResetHistory() {
+ for (int i = 0; i < thread_count_; ++i) {
+ assignment_history_[i] = 0;
+ completion_history_[i] = 0;
+ }
+}
+
+int WorkQueue::GetMinCompletionsByWorkerThread() const {
+ int minumum = completion_history_[0];
+ for (int i = 0; i < thread_count_; ++i)
+ minumum = std::min(minumum, completion_history_[i]);
+ return minumum;
+}
+
+int WorkQueue::GetMaxCompletionsByWorkerThread() const {
+ int maximum = completion_history_[0];
+ for (int i = 0; i < thread_count_; ++i)
+ maximum = std::max(maximum, completion_history_[i]);
+ return maximum;
+}
+
+int WorkQueue::GetNumThreadsTakingAssignments() const {
+ int count = 0;
+ for (int i = 0; i < thread_count_; ++i)
+ if (assignment_history_[i])
+ count++;
+ return count;
+}
+
+int WorkQueue::GetNumThreadsCompletingTasks() const {
+ int count = 0;
+ for (int i = 0; i < thread_count_; ++i)
+ if (completion_history_[i])
+ count++;
+ return count;
+}
+
+int WorkQueue::GetNumberOfCompletedTasks() const {
+ int total = 0;
+ for (int i = 0; i < thread_count_; ++i)
+ total += completion_history_[i];
+ return total;
+}
+
+void WorkQueue::SetWorkTime(TimeDelta delay) {
+ worker_delay_ = delay;
+}
+
+void WorkQueue::SetTaskCount(int count) {
+ task_count_ = count;
+}
+
+void WorkQueue::SetAllowHelp(bool allow) {
+ allow_help_requests_ = allow;
+}
+
+void WorkQueue::SetShutdown() {
+ shutdown_ = true;
+}
+
+} // namespace
diff --git a/base/data/file_util_unittest/binary_file.bin b/base/data/file_util_unittest/binary_file.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file.bin
Binary files differ
diff --git a/base/data/file_util_unittest/binary_file_diff.bin b/base/data/file_util_unittest/binary_file_diff.bin
new file mode 100644
index 0000000..103b26d
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file_diff.bin
Binary files differ
diff --git a/base/data/file_util_unittest/binary_file_same.bin b/base/data/file_util_unittest/binary_file_same.bin
new file mode 100644
index 0000000..f53cc82
--- /dev/null
+++ b/base/data/file_util_unittest/binary_file_same.bin
Binary files differ
diff --git a/base/data/file_util_unittest/different.txt b/base/data/file_util_unittest/different.txt
new file mode 100644
index 0000000..5b9f9c4
--- /dev/null
+++ b/base/data/file_util_unittest/different.txt
@@ -0,0 +1 @@
+This file is different.
diff --git a/base/data/file_util_unittest/different_first.txt b/base/data/file_util_unittest/different_first.txt
new file mode 100644
index 0000000..8661d66
--- /dev/null
+++ b/base/data/file_util_unittest/different_first.txt
@@ -0,0 +1 @@
+this file is the same.
diff --git a/base/data/file_util_unittest/different_last.txt b/base/data/file_util_unittest/different_last.txt
new file mode 100644
index 0000000..e8b3e5a
--- /dev/null
+++ b/base/data/file_util_unittest/different_last.txt
@@ -0,0 +1 @@
+This file is the same. \ No newline at end of file
diff --git a/base/data/file_util_unittest/empty1.txt b/base/data/file_util_unittest/empty1.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/file_util_unittest/empty1.txt
diff --git a/base/data/file_util_unittest/empty2.txt b/base/data/file_util_unittest/empty2.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/file_util_unittest/empty2.txt
diff --git a/base/data/file_util_unittest/original.txt b/base/data/file_util_unittest/original.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/data/file_util_unittest/original.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/data/file_util_unittest/same.txt b/base/data/file_util_unittest/same.txt
new file mode 100644
index 0000000..4422f57
--- /dev/null
+++ b/base/data/file_util_unittest/same.txt
@@ -0,0 +1 @@
+This file is the same.
diff --git a/base/data/file_util_unittest/same_length.txt b/base/data/file_util_unittest/same_length.txt
new file mode 100644
index 0000000..157405c
--- /dev/null
+++ b/base/data/file_util_unittest/same_length.txt
@@ -0,0 +1 @@
+This file is not same.
diff --git a/base/data/file_util_unittest/shortened.txt b/base/data/file_util_unittest/shortened.txt
new file mode 100644
index 0000000..2bee82c
--- /dev/null
+++ b/base/data/file_util_unittest/shortened.txt
@@ -0,0 +1 @@
+This file is the \ No newline at end of file
diff --git a/base/data/file_version_info_unittest/FileVersionInfoTest1.dll b/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
new file mode 100644
index 0000000..bdf8dc0
--- /dev/null
+++ b/base/data/file_version_info_unittest/FileVersionInfoTest1.dll
Binary files differ
diff --git a/base/data/file_version_info_unittest/FileVersionInfoTest2.dll b/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
new file mode 100644
index 0000000..51e7966
--- /dev/null
+++ b/base/data/file_version_info_unittest/FileVersionInfoTest2.dll
Binary files differ
diff --git a/base/data/purify/base_unittests.exe.gtest.txt b/base/data/purify/base_unittests.exe.gtest.txt
new file mode 100644
index 0000000..0687e7e
--- /dev/null
+++ b/base/data/purify/base_unittests.exe.gtest.txt
@@ -0,0 +1,19 @@
+# this test causes Purify to get completely confused, aborting the test and
+# popping up 10 or more error dialogs
+StatsTableTest.MultipleProcesses
+
+# see bug 1151158
+# causes purify to occasionally crash, possibly the same reason as 1110206 below
+StatsTableTest.MultipleThreads
+
+# this test takes a really long time to run in Purify
+TimeTicks.Rollover
+
+# see bug 1110206
+ConditionVariableTest.LargeFastTaskTest
+
+# see bug 1150075
+MessageLoopTest.Crasher*
+
+# see bug 1195707
+WMIUtilTest.*
diff --git a/base/data/purify/base_unittests.exe_MLK.txt b/base/data/purify/base_unittests.exe_MLK.txt
new file mode 100644
index 0000000..e622502
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK.txt
@@ -0,0 +1,97 @@
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::~LogMessage(void)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::~LogMessage(void)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+CoTaskMemAlloc [OLE32.DLL]
+Alloc Location
+ ...
+ base/wmi_util.cc WMIUtil::CreateLocalConnection(bool)
+ base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *)
+ base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
diff --git a/base/data/purify/base_unittests.exe_MLK_flakey.txt b/base/data/purify/base_unittests.exe_MLK_flakey.txt
new file mode 100644
index 0000000..0626ba0
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK_flakey.txt
@@ -0,0 +1,8 @@
+CoTaskMemAlloc [OLE32.DLL]
+Alloc Location
+ ...
+ base/wmi_util.cc WMIUtil::CreateLocalConnection(bool)
+ base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *)
+ base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
diff --git a/base/data/purify/base_unittests.exe_MLK_ignore.txt b/base/data/purify/base_unittests.exe_MLK_ignore.txt
new file mode 100644
index 0000000..020fadb
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_MLK_ignore.txt
@@ -0,0 +1,93 @@
+# -----
+# Leaks in ::RaiseException, called when we log a fatal error. See bug 1078612.
+
+std::strstreambuf::overflow(int) [base_unittests.exe]
+Alloc Location
+ ...
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::~LogMessage(void)
+ base/check_handler_unittest.cc ThisFunctionAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::strstreambuf::overflow(int) [base_unittests.exe]
+Alloc Location
+ ...
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::_Mutex::_Mutex(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::basic_streambuf<char,char_traits<char>::std>::basic_streambuf<char,char_traits<char>::std>(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::ios_base::_Init(void) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::LogMessage(char const*,int,int)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+std::D::_Allocate(unsigned int,char *) [base_unittests.exe]
+Alloc Location
+ ...
+ base/logging.cc logging::LogMessage::~LogMessage(void)
+ base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void)
+ base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
+# End of leaks in ::RaiseException
+# -----
diff --git a/base/data/purify/base_unittests.exe_PAR_ignore.txt b/base/data/purify/base_unittests.exe_PAR_ignore.txt
new file mode 100644
index 0000000..69018f2
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_PAR_ignore.txt
@@ -0,0 +1,8 @@
+# Probably a Purify error. See bug 1076843.
+WideCharToMultiByte: Invalid size (0x27) for destination buffer.
+Error Location
+ ...
+ base/file_util_unittest.cc FileUtilTest_ResolveShortcutTest_Test::TestBody(void)
+ testing/gtest/src/gtest.cc testing::Test::Run(void)
+ ^^^
+
diff --git a/base/data/purify/base_unittests.exe_UMR.txt b/base/data/purify/base_unittests.exe_UMR.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/base/data/purify/base_unittests.exe_UMR.txt
diff --git a/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png b/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/00_pc_clean.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png b/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/00_vc_clean.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png b/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png b/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
new file mode 100644
index 0000000..a5435f2
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png b/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
new file mode 100644
index 0000000..c21fdf1
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png b/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
new file mode 100644
index 0000000..c21fdf1
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png b/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png b/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
new file mode 100644
index 0000000..dfc46a8
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
new file mode 100644
index 0000000..69cc6dc
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png b/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
new file mode 100644
index 0000000..69cc6dc
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png b/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png b/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png b/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
new file mode 100644
index 0000000..bbdfc36
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png b/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
new file mode 100644
index 0000000..bbdfc36
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png b/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
new file mode 100644
index 0000000..9dc35f0
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
Binary files differ
diff --git a/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png b/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
new file mode 100644
index 0000000..9dc35f0
--- /dev/null
+++ b/base/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png b/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png
new file mode 100644
index 0000000..812b1ca
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/00_pc_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png b/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png
new file mode 100644
index 0000000..812b1ca
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/00_vc_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png b/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png
new file mode 100644
index 0000000..1d1342b
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/01_pc_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png b/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png
new file mode 100644
index 0000000..1d1342b
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/01_vc_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png b/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png
new file mode 100644
index 0000000..a19d09d
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/bitmap_alpha.png
Binary files differ
diff --git a/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png b/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png
new file mode 100644
index 0000000..3560d27
--- /dev/null
+++ b/base/data/vectorcanvastest/bitmaps/bitmap_opaque.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png b/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png
new file mode 100644
index 0000000..896631b
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/00_pc_circle_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png b/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png
new file mode 100644
index 0000000..d1d4cb2
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/00_vc_circle_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/01_pc_circle_fill.png b/base/data/vectorcanvastest/circles/01_pc_circle_fill.png
new file mode 100644
index 0000000..a317292
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/01_pc_circle_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/01_vc_circle_fill.png b/base/data/vectorcanvastest/circles/01_vc_circle_fill.png
new file mode 100644
index 0000000..0628c02
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/01_vc_circle_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png b/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
new file mode 100644
index 0000000..64ae06a
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png b/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
new file mode 100644
index 0000000..f333f9d
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png b/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
new file mode 100644
index 0000000..66d6a33
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png b/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
new file mode 100644
index 0000000..a756cf3
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png b/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
new file mode 100644
index 0000000..de9c492
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png b/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
new file mode 100644
index 0000000..ae3cd03
--- /dev/null
+++ b/base/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png b/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/00_pc_clipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png b/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/00_vc_clipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png b/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
new file mode 100644
index 0000000..436f9a5
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png b/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
new file mode 100644
index 0000000..436f9a5
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png b/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingcombined/00_pc_combined.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png b/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png
new file mode 100644
index 0000000..14ff949
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingcombined/00_vc_combined.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png b/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
new file mode 100644
index 0000000..704f03a
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png b/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
new file mode 100644
index 0000000..704f03a
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingpath/00_pc_path.png b/base/data/vectorcanvastest/clippingpath/00_pc_path.png
new file mode 100644
index 0000000..78443af
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingpath/00_pc_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingpath/00_vc_path.png b/base/data/vectorcanvastest/clippingpath/00_vc_path.png
new file mode 100644
index 0000000..78443af
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingpath/00_vc_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingrect/00_pc_rect.png b/base/data/vectorcanvastest/clippingrect/00_pc_rect.png
new file mode 100644
index 0000000..9c365e1
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingrect/00_pc_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/clippingrect/00_vc_rect.png b/base/data/vectorcanvastest/clippingrect/00_vc_rect.png
new file mode 100644
index 0000000..9c365e1
--- /dev/null
+++ b/base/data/vectorcanvastest/clippingrect/00_vc_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png b/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
new file mode 100644
index 0000000..5736c35
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png b/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
new file mode 100644
index 0000000..5736c35
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png b/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
new file mode 100644
index 0000000..bfffd8a
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png b/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
new file mode 100644
index 0000000..ae6b753
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png b/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
new file mode 100644
index 0000000..75acdad
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png b/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
new file mode 100644
index 0000000..86a6799
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png b/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
new file mode 100644
index 0000000..50502cc
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png b/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
new file mode 100644
index 0000000..362f6e7
--- /dev/null
+++ b/base/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png b/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
new file mode 100644
index 0000000..7bcd998
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png b/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
new file mode 100644
index 0000000..46c9b0a
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png b/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png
new file mode 100644
index 0000000..09f41db
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/01_pc_vertical.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png b/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png
new file mode 100644
index 0000000..7f5f1f7
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/01_vc_vertical.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png b/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
new file mode 100644
index 0000000..5966df6
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png b/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
new file mode 100644
index 0000000..e43a844
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png b/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
new file mode 100644
index 0000000..9ac4825
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png b/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
new file mode 100644
index 0000000..d9e033a
--- /dev/null
+++ b/base/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/00_pc_translate1.png b/base/data/vectorcanvastest/matrix/00_pc_translate1.png
new file mode 100644
index 0000000..fe27cb3
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/00_pc_translate1.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/00_vc_translate1.png b/base/data/vectorcanvastest/matrix/00_vc_translate1.png
new file mode 100644
index 0000000..fe27cb3
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/00_vc_translate1.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/01_pc_translate2.png b/base/data/vectorcanvastest/matrix/01_pc_translate2.png
new file mode 100644
index 0000000..406bf57
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/01_pc_translate2.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/01_vc_translate2.png b/base/data/vectorcanvastest/matrix/01_vc_translate2.png
new file mode 100644
index 0000000..406bf57
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/01_vc_translate2.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/02_pc_scale.png b/base/data/vectorcanvastest/matrix/02_pc_scale.png
new file mode 100644
index 0000000..9e94fb0
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/02_pc_scale.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/02_vc_scale.png b/base/data/vectorcanvastest/matrix/02_vc_scale.png
new file mode 100644
index 0000000..fde62aa
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/02_vc_scale.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/03_pc_rotate.png b/base/data/vectorcanvastest/matrix/03_pc_rotate.png
new file mode 100644
index 0000000..7a43a2a
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/03_pc_rotate.png
Binary files differ
diff --git a/base/data/vectorcanvastest/matrix/03_vc_rotate.png b/base/data/vectorcanvastest/matrix/03_vc_rotate.png
new file mode 100644
index 0000000..7a22b7f
--- /dev/null
+++ b/base/data/vectorcanvastest/matrix/03_vc_rotate.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png b/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png
new file mode 100644
index 0000000..e08d3e2
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/00_pc_dash_line.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png b/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png
new file mode 100644
index 0000000..e08d3e2
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/00_vc_dash_line.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png b/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png
new file mode 100644
index 0000000..3a301354
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/01_pc_dash_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png b/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png
new file mode 100644
index 0000000..7868b9a
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/01_vc_dash_path.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png b/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
new file mode 100644
index 0000000..04f2ceb
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png b/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
new file mode 100644
index 0000000..5344eee
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/03_pc_circle.png b/base/data/vectorcanvastest/patheffects/03_pc_circle.png
new file mode 100644
index 0000000..90fa28b
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/03_pc_circle.png
Binary files differ
diff --git a/base/data/vectorcanvastest/patheffects/03_vc_circle.png b/base/data/vectorcanvastest/patheffects/03_vc_circle.png
new file mode 100644
index 0000000..8027297
--- /dev/null
+++ b/base/data/vectorcanvastest/patheffects/03_vc_circle.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png b/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
new file mode 100644
index 0000000..cefbf87
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png b/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
new file mode 100644
index 0000000..cefbf87
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png b/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
new file mode 100644
index 0000000..7bcd998
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
Binary files differ
diff --git a/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png b/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
new file mode 100644
index 0000000..46c9b0a
--- /dev/null
+++ b/base/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
Binary files differ
diff --git a/base/data/vectorcanvastest/uninitialized/00_pc_empty.png b/base/data/vectorcanvastest/uninitialized/00_pc_empty.png
new file mode 100644
index 0000000..dec6694
--- /dev/null
+++ b/base/data/vectorcanvastest/uninitialized/00_pc_empty.png
Binary files differ
diff --git a/base/data/vectorcanvastest/uninitialized/00_vc_empty.png b/base/data/vectorcanvastest/uninitialized/00_vc_empty.png
new file mode 100644
index 0000000..9cbff6e
--- /dev/null
+++ b/base/data/vectorcanvastest/uninitialized/00_vc_empty.png
Binary files differ
diff --git a/base/debug_message.cc b/base/debug_message.cc
new file mode 100644
index 0000000..e725dcf
--- /dev/null
+++ b/base/debug_message.cc
@@ -0,0 +1,42 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+// Display the command line. This program is designed to be called from
+// another process to display assertions. Since the other process has
+// complete control of our command line, we assume that it did *not*
+// add the program name as the first parameter. This allows us to just
+// show the command line directly as the message.
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine, int nCmdShow) {
+ LPWSTR cmdline = GetCommandLineW();
+ MessageBox(NULL, cmdline, L"Kr\x00d8m", MB_TOPMOST);
+ return 0;
+}
diff --git a/base/debug_on_start.cc b/base/debug_on_start.cc
new file mode 100644
index 0000000..7ae6cd1
--- /dev/null
+++ b/base/debug_on_start.cc
@@ -0,0 +1,89 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+
+#include "base/debug_on_start.h"
+
+#include "base/base_switches.h"
+#include "base/basictypes.h"
+#include "base/debug_util.h"
+
+// Minimalist implementation to try to find a command line argument. We can use
+// kernel32 exported functions but not the CRT functions because we're too early
+// in the process startup.
+// The code is not that bright and will find things like ---argument or
+// /-/argument.
+// Note: command_line is non-destructively modified.
+bool DebugOnStart::FindArgument(wchar_t* command_line, const wchar_t* argument)
+{
+ int argument_len = lstrlen(argument);
+ int command_line_len = lstrlen(command_line);
+ while (command_line_len > argument_len) {
+ wchar_t first_char = command_line[0];
+ wchar_t last_char = command_line[argument_len+1];
+ // Try to find an argument.
+ if ((first_char == L'-' || first_char == L'/') &&
+ (last_char == L' ' || last_char == 0 || last_char == L'=')) {
+ command_line[argument_len+1] = 0;
+ // Skip the - or /
+ if (lstrcmpi(command_line+1, argument) == 0) {
+ // Found it.
+ command_line[argument_len+1] = last_char;
+ return true;
+ }
+ // Fix back.
+ command_line[argument_len+1] = last_char;
+ }
+ // Continue searching.
+ ++command_line;
+ --command_line_len;
+ }
+ return false;
+}
+
+// static
+int __cdecl DebugOnStart::Init() {
+ // Try to find the argument.
+ if (FindArgument(GetCommandLine(), switches::kDebugOnStart)) {
+ // We can do 2 things here:
+ // - Ask for a debugger to attach to us. This involve reading the registry
+ // key and creating the process.
+ // - Do a int3.
+
+ // It will fails if we run in a sandbox. That is expected.
+ DebugUtil::SpawnDebuggerOnProcess(GetCurrentProcessId());
+
+ // Wait for a debugger to come take us.
+ DebugUtil::WaitForDebugger(60, false);
+ } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) {
+ // Wait for a debugger to come take us.
+ DebugUtil::WaitForDebugger(60, true);
+ }
+ return 0;
+}
diff --git a/base/debug_on_start.h b/base/debug_on_start.h
new file mode 100644
index 0000000..87afe9b
--- /dev/null
+++ b/base/debug_on_start.h
@@ -0,0 +1,75 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Define the necessary code and global data to look for kDebugOnStart command
+// line argument. When the command line argument is detected, it invokes the
+// debugger, if no system-wide debugger is registered, a debug break is done.
+
+#ifndef BASE_DEBUG_ON_START_H__
+#define BASE_DEBUG_ON_START_H__
+
+// This only works on Windows.
+#ifdef _WIN32
+
+#ifndef DECLSPEC_SELECTANY
+#define DECLSPEC_SELECTANY __declspec(selectany)
+#endif
+
+// Debug on start functions and data.
+class DebugOnStart {
+ public:
+ // Expected function type in the .CRT$XI* section.
+ // Note: See VC\crt\src\internal.h for reference.
+ typedef int (__cdecl *PIFV)(void);
+
+ // Looks at the command line for kDebugOnStart argument. If found, it invokes
+ // the debugger, if this fails, it crashes.
+ static int __cdecl Init();
+
+ // Returns true if the 'argument' is present in the 'command_line'. It does
+ // not use the CRT, only Kernel32 functions.
+ static bool FindArgument(wchar_t* command_line, const wchar_t* argument);
+};
+
+// Set the function pointer to our function to look for a crash on start. The
+// XIB section is started pretty early in the program initialization so in
+// theory it should be called before any user created global variable
+// initialization code and CRT initialization code.
+// Note: See VC\crt\src\defsects.inc and VC\crt\src\crt0.c for reference.
+
+// "Fix" the data section.
+#pragma data_seg(push, ".CRT$XIB")
+// Declare the pointer so the CRT will find it.
+DECLSPEC_SELECTANY DebugOnStart::PIFV debug_on_start = &DebugOnStart::Init;
+// Fix back the data segment.
+#pragma data_seg(pop)
+
+#endif // _WIN32
+
+#endif // BASE_DEBUG_ON_START_H__
diff --git a/base/debug_util.cc b/base/debug_util.cc
new file mode 100644
index 0000000..fb60f5a
--- /dev/null
+++ b/base/debug_util.cc
@@ -0,0 +1,132 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/debug_util.h"
+
+namespace {
+
+// Minimalist key reader.
+// Note: Does not use the CRT.
+bool RegReadString(HKEY root, const wchar_t* subkey,
+ const wchar_t* value_name, wchar_t* buffer, int* len) {
+ HKEY key = NULL;
+ DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key);
+ if (ERROR_SUCCESS != res || key == NULL)
+ return false;
+
+ DWORD type = 0;
+ DWORD buffer_size = *len * sizeof(wchar_t);
+ // We don't support REG_EXPAND_SZ.
+ res = RegQueryValueEx(key, value_name, NULL, &type,
+ reinterpret_cast<BYTE*>(buffer), &buffer_size);
+ if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) {
+ // Make sure the buffer is NULL terminated.
+ buffer[*len - 1] = 0;
+ *len = lstrlen(buffer);
+ RegCloseKey(key);
+ return true;
+ }
+ RegCloseKey(key);
+ return false;
+}
+
+// Replaces each "%ld" in input per a value. Not efficient but it works.
+// Note: Does not use the CRT.
+bool StringReplace(const wchar_t* input, int value, wchar_t* output,
+ int output_len) {
+ memset(output, 0, output_len*sizeof(wchar_t));
+ int input_len = lstrlen(input);
+
+ for (int i = 0; i < input_len; ++i) {
+ int current_output_len = lstrlen(output);
+
+ if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') {
+ // Make sure we have enough place left.
+ if ((current_output_len + 12) >= output_len)
+ return false;
+
+ // Cheap _itow().
+ wsprintf(output+current_output_len, L"%d", value);
+ i += 2;
+ } else {
+ if (current_output_len >= output_len)
+ return false;
+ output[current_output_len] = input[i];
+ }
+ }
+ return true;
+}
+
+} // namespace
+
+// Note: Does not use the CRT.
+bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) {
+ wchar_t reg_value[1026];
+ int len = arraysize(reg_value);
+ if (RegReadString(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug",
+ L"Debugger", reg_value, &len)) {
+ wchar_t command_line[1026];
+ if (StringReplace(reg_value, process_id, command_line,
+ arraysize(command_line))) {
+ // We don't mind if the debugger is present because it will simply fail
+ // to attach to this process.
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ PROCESS_INFORMATION process_info = {0};
+
+ if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL,
+ &startup_info, &process_info)) {
+ CloseHandle(process_info.hThread);
+ WaitForInputIdle(process_info.hProcess, 10000);
+ CloseHandle(process_info.hProcess);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Note: Does not use the CRT.
+bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) {
+ for (int i = 0; i < wait_seconds * 10; ++i) {
+ if (IsDebuggerPresent()) {
+ if (!silent) {
+ // If you hit here, you just use -debug-on-start or -debug-children.
+ __debugbreak();
+ }
+ return true;
+ }
+ Sleep(100);
+ }
+ return false;
+}
diff --git a/base/debug_util.h b/base/debug_util.h
new file mode 100644
index 0000000..54381e5
--- /dev/null
+++ b/base/debug_util.h
@@ -0,0 +1,44 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_DEBUG_UTIL_H__
+#define BASE_DEBUG_UTIL_H__
+
+class DebugUtil {
+ public:
+ // Starts the registered system-wide JIT debugger to attach it to specified
+ // process.
+ static bool SpawnDebuggerOnProcess(unsigned process_id);
+
+ // Waits wait_seconds seconds for a debugger to attach to the current process.
+ // When silent is false, an exception is thrown when a debugger is detected.
+ static bool WaitForDebugger(int wait_seconds, bool silent);
+};
+
+#endif // BASE_DEBUG_UTIL_H__
diff --git a/base/event_recorder.cc b/base/event_recorder.cc
new file mode 100644
index 0000000..b4c89a3
--- /dev/null
+++ b/base/event_recorder.cc
@@ -0,0 +1,281 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/event_recorder.h"
+
+#include <mmsystem.h>
+
+#include "base/logging.h"
+#include "base/time.h"
+
+// A note about time.
+// For perfect playback of events, you'd like a very accurate timer
+// so that events are played back at exactly the same time that
+// they were recorded. However, windows has a clock which is only
+// granular to ~15ms. We see more consistent event playback when
+// using a higher resolution timer. To do this, we use the
+// timeGetTime API instead of the default GetTickCount() API.
+
+namespace base {
+
+EventRecorder* EventRecorder::current_ = NULL;
+
+LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam,
+ LPARAM lParam) {
+ CHECK(EventRecorder::current());
+ return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam);
+}
+
+LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam,
+ LPARAM lParam) {
+ CHECK(EventRecorder::current());
+ return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam);
+}
+
+EventRecorder::~EventRecorder() {
+ // Try to assert early if the caller deletes the recorder
+ // while it is still in use.
+ DCHECK(!journal_hook_);
+ DCHECK(!is_recording_ && !is_playing_);
+}
+
+bool EventRecorder::StartRecording(std::wstring& filename) {
+ if (journal_hook_ != NULL)
+ return false;
+ if (is_recording_ || is_playing_)
+ return false;
+
+ // Open the recording file.
+ DCHECK(file_ == NULL);
+ if (_wfopen_s(&file_, filename.c_str(), L"wb+") != 0) {
+ DLOG(ERROR) << "EventRecorder could not open log file";
+ return false;
+ }
+
+ // Set the faster clock, if possible.
+ ::timeBeginPeriod(1);
+
+ // Set the recording hook. JOURNALRECORD can only be used as a global hook.
+ journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc,
+ GetModuleHandle(NULL), 0);
+ if (!journal_hook_) {
+ DLOG(ERROR) << "EventRecorder Record Hook failed";
+ fclose(file_);
+ return false;
+ }
+
+ is_recording_ = true;
+ return true;
+}
+
+void EventRecorder::StopRecording() {
+ if (is_recording_) {
+ DCHECK(journal_hook_ != NULL);
+
+ if (!::UnhookWindowsHookEx(journal_hook_)) {
+ DLOG(ERROR) << "EventRecorder Unhook failed";
+ // Nothing else we can really do here.
+ return;
+ }
+
+ ::timeEndPeriod(1);
+
+ DCHECK(file_ != NULL);
+ fclose(file_);
+ file_ = NULL;
+
+ journal_hook_ = NULL;
+ is_recording_ = false;
+ }
+}
+
+bool EventRecorder::StartPlayback(std::wstring& filename) {
+ if (journal_hook_ != NULL)
+ return false;
+ if (is_recording_ || is_playing_)
+ return false;
+
+ // Open the recording file.
+ DCHECK(file_ == NULL);
+ if (_wfopen_s(&file_, filename.c_str(), L"rb") != 0) {
+ DLOG(ERROR) << "EventRecorder Playback could not open log file";
+ return false;
+ }
+ // Read the first event from the record.
+ if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) {
+ DLOG(ERROR) << "EventRecorder Playback has no records!";
+ fclose(file_);
+ return false;
+ }
+
+ // Set the faster clock, if possible.
+ ::timeBeginPeriod(1);
+
+ // Playback time is tricky. When playing back, we read a series of events,
+ // each with timeouts. Simply subtracting the delta between two timers will
+ // lead to fast playback (about 2x speed). The API has two events, one
+ // which advances to the next event (HC_SKIP), and another that requests the
+ // event (HC_GETNEXT). The same event will be requested multiple times.
+ // Each time the event is requested, we must calculate the new delay.
+ // To do this, we track the start time of the playback, and constantly
+ // re-compute the delay. I mention this only because I saw two examples
+ // of how to use this code on the net, and both were broken :-)
+ playback_start_time_ = timeGetTime();
+ playback_first_msg_time_ = playback_msg_.time;
+
+ // Set the hook. JOURNALPLAYBACK can only be used as a global hook.
+ journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc,
+ GetModuleHandle(NULL), 0);
+ if (!journal_hook_) {
+ DLOG(ERROR) << "EventRecorder Playback Hook failed";
+ return false;
+ }
+
+ is_playing_ = true;
+
+ return true;
+}
+
+void EventRecorder::StopPlayback() {
+ if (is_playing_) {
+ DCHECK(journal_hook_ != NULL);
+
+ if (!::UnhookWindowsHookEx(journal_hook_)) {
+ DLOG(ERROR) << "EventRecorder Unhook failed";
+ // Nothing else we can really do here.
+ }
+
+ DCHECK(file_ != NULL);
+ fclose(file_);
+ file_ = NULL;
+
+ ::timeEndPeriod(1);
+
+ journal_hook_ = NULL;
+ is_playing_ = false;
+ }
+}
+
+// Windows callback hook for the recorder.
+LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ static bool recording_enabled = true;
+ EVENTMSG *msg_ptr = NULL;
+
+ // The API says we have to do this.
+ // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx
+ if (nCode < 0)
+ return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+
+ // Check for the break key being pressed and stop recording.
+ if (::GetKeyState(VK_CANCEL) & 0x8000) {
+ StopRecording();
+ return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+ }
+
+ // The Journal Recorder must stop recording events when system modal
+ // dialogs are present. (see msdn link above)
+ switch(nCode)
+ {
+ case HC_SYSMODALON:
+ recording_enabled = false;
+ break;
+ case HC_SYSMODALOFF:
+ recording_enabled = true;
+ break;
+ }
+
+ if (nCode == HC_ACTION && recording_enabled) {
+ // Aha - we have an event to record.
+ msg_ptr = reinterpret_cast<EVENTMSG*>(lParam);
+ msg_ptr->time = timeGetTime();
+ fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_);
+ fflush(file_);
+ }
+
+ return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+}
+
+// Windows callback for the playback mode.
+LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam)
+{
+ static bool playback_enabled = true;
+ int delay = 0;
+
+ switch(nCode)
+ {
+ // A system modal dialog box is being displayed. Stop playing back
+ // messages.
+ case HC_SYSMODALON:
+ playback_enabled = false;
+ break;
+
+ // A system modal dialog box is destroyed. We can start playing back
+ // messages again.
+ case HC_SYSMODALOFF:
+ playback_enabled = true;
+ break;
+
+ // Prepare to copy the next mouse or keyboard event to playback.
+ case HC_SKIP:
+ if (!playback_enabled)
+ break;
+
+ // Read the next event from the record.
+ if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1)
+ this->StopPlayback();
+ break;
+
+ // Copy the mouse or keyboard event to the EVENTMSG structure in lParam.
+ case HC_GETNEXT:
+ if (!playback_enabled)
+ break;
+
+ memcpy(reinterpret_cast<void*>(lParam), &playback_msg_, sizeof(playback_msg_));
+
+ // The return value is the amount of time (in milliseconds) to wait
+ // before playing back the next message in the playback queue. Each
+ // time this is called, we recalculate the delay relative to our current
+ // wall clock.
+ delay = (playback_msg_.time - playback_first_msg_time_) -
+ (timeGetTime() - playback_start_time_);
+ if (delay < 0)
+ delay = 0;
+ return delay;
+
+ // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE
+ // indicating that the message is not removed from the message queue after
+ // PeekMessage processing.
+ case HC_NOREMOVE:
+ break;
+ }
+
+ return CallNextHookEx(journal_hook_, nCode, wParam, lParam);
+}
+
+} // namespace base \ No newline at end of file
diff --git a/base/event_recorder.h b/base/event_recorder.h
new file mode 100644
index 0000000..71b815f
--- /dev/null
+++ b/base/event_recorder.h
@@ -0,0 +1,115 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_EVENT_RECORDER_H_
+#define BASE_EVENT_RECORDER_H_
+
+#include <string>
+#include <windows.h>
+#include "base/basictypes.h"
+
+namespace base {
+
+// A class for recording and playing back keyboard and mouse input events.
+//
+// Note - if you record events, and the playback with the windows in
+// different sizes or positions, the playback will fail. When
+// recording and playing, you should move the relevant windows
+// to constant sizes and locations.
+// TODO(mbelshe) For now this is a singleton. I believe that this class
+// could be easily modified to:
+// support two simultaneous recorders
+// be playing back events while already recording events.
+// Why? Imagine if the product had a "record a macro" feature.
+// You might be recording globally, while recording or playing back
+// a macro. I don't think two playbacks make sense.
+class EventRecorder {
+ public:
+ // Get the singleton EventRecorder.
+ // We can only handle one recorder/player at a time.
+ static EventRecorder* current() {
+ if (!current_)
+ current_ = new EventRecorder();
+ return current_;
+ }
+
+ // Starts recording events.
+ // Will clobber the file if it already exists.
+ // Returns true on success, or false if an error occurred.
+ bool StartRecording(std::wstring &filename);
+
+ // Stops recording.
+ void StopRecording();
+
+ // Is the EventRecorder currently recording.
+ bool is_recording() const { return is_recording_; }
+
+ // Plays events previously recorded.
+ // Returns true on success, or false if an error occurred.
+ bool StartPlayback(std::wstring &filename);
+
+ // Stops playback.
+ void StopPlayback();
+
+ // Is the EventRecorder currently playing.
+ bool is_playing() const { return is_playing_; }
+
+ // C-style callbacks for the EventRecorder.
+ // Used for internal purposes only.
+ LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+ LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam);
+
+ private:
+ // Create a new EventRecorder. Events are saved to the file filename.
+ // If the file already exists, it will be deleted before recording
+ // starts.
+ explicit EventRecorder()
+ : is_recording_(false),
+ is_playing_(false),
+ journal_hook_(NULL),
+ file_(NULL) {
+ }
+ ~EventRecorder();
+
+ static EventRecorder* current_; // Our singleton.
+
+ bool is_recording_;
+ bool is_playing_;
+ HHOOK journal_hook_;
+ FILE* file_;
+ EVENTMSG playback_msg_;
+ int playback_first_msg_time_;
+ int playback_start_time_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(EventRecorder);
+};
+
+} // namespace base
+
+#endif // BASE_EVENT_RECORDER_H_
diff --git a/base/file_util.cc b/base/file_util.cc
new file mode 100644
index 0000000..83cf534
--- /dev/null
+++ b/base/file_util.cc
@@ -0,0 +1,852 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#include <time.h>
+#include <fstream>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "unicode/uniset.h"
+
+using std::wstring;
+
+namespace file_util {
+
+const wchar_t kPathSeparator = L'\\';
+const wchar_t kExtensionSeparator = L'.';
+
+bool EndsWithSeparator(std::wstring* path) {
+ return (!path->empty() && (*path)[path->length() - 1] == kPathSeparator);
+}
+
+void TrimTrailingSeparator(std::wstring* dir) {
+ while (EndsWithSeparator(dir))
+ dir->resize(dir->length() - 1);
+}
+
+void UpOneDirectory(std::wstring* dir) {
+ TrimTrailingSeparator(dir);
+
+ wstring::size_type last_sep = dir->find_last_of(kPathSeparator);
+ if (last_sep != wstring::npos)
+ dir->resize(last_sep);
+}
+
+void UpOneDirectoryOrEmpty(std::wstring* dir) {
+ TrimTrailingSeparator(dir);
+
+ wstring::size_type last_sep = dir->find_last_of(kPathSeparator);
+ if (last_sep != wstring::npos)
+ dir->resize(last_sep);
+ else
+ dir->clear();
+}
+
+void TrimFilename(std::wstring* path) {
+ if (EndsWithSeparator(path)) {
+ TrimTrailingSeparator(path);
+ } else {
+ wstring::size_type last_sep = path->find_last_of(kPathSeparator);
+ if (last_sep != wstring::npos)
+ path->resize(last_sep);
+ }
+}
+
+// TODO(mpcomplete): Make this platform-independent, etc.
+wstring GetFilenameFromPath(const wstring& path) {
+ wstring::size_type pos = path.find_last_of(L"\\/");
+ return wstring(path, pos == wstring::npos ? 0 : pos+1);
+}
+
+wstring GetFileExtensionFromPath(const wstring& path) {
+ wstring file_name = GetFilenameFromPath(path);
+ wstring::size_type last_dot = file_name.rfind(L'.');
+ return wstring(last_dot == wstring::npos? L"" : file_name, last_dot+1);
+}
+
+void AppendToPath(std::wstring* path, const std::wstring& new_ending) {
+ if (!path) {
+ NOTREACHED();
+ return; // Don't crash in this function in release builds.
+ }
+
+ if (!EndsWithSeparator(path))
+ path->push_back(kPathSeparator);
+ path->append(new_ending);
+}
+
+void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix) {
+ DCHECK(path);
+
+ const wstring::size_type last_dot = path->rfind(kExtensionSeparator);
+ const wstring::size_type last_sep = path->rfind(kPathSeparator);
+
+ if (last_dot == wstring::npos ||
+ (last_sep != wstring::npos && last_dot < last_sep)) {
+ // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo".
+ // We should just append the suffix to the entire path.
+ path->append(suffix);
+ return;
+ }
+
+ path->insert(last_dot, suffix);
+}
+
+void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) {
+ DCHECK(file_name);
+
+ // Control characters, formating characters, non-characters, and
+ // some printable ASCII characters regarded as dangerous ('"*/:<>?\\').
+ // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx
+ // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx
+ // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they
+ // are legitimate in Arabic and some S/SE Asian scripts. However, when used
+ // elsewhere, they can be confusing/problematic.
+ // Also, consider wrapping the set with our Singleton class to create and
+ // freeze it only once. Note that there's a trade-off between memory and
+ // speed.
+
+ UErrorCode status = U_ZERO_ERROR;
+#ifdef U_WCHAR_IS_UTF16
+ UnicodeSet illegal_characters(UnicodeString(
+ L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status);
+#else
+ UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE(
+ "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status);
+#endif
+ DCHECK(U_SUCCESS(status));
+ // Add non-characters. If this becomes a performance bottleneck by
+ // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead.
+ illegal_characters.add(0xFDD0, 0xFDEF);
+ for (int i = 0; i <= 0x10; ++i) {
+ int plane_base = 0x10000 * i;
+ illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF);
+ }
+ illegal_characters.freeze();
+ DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000);
+
+ // Remove leading and trailing whitespace.
+ TrimWhitespace(*file_name, TRIM_ALL, file_name);
+
+ std::wstring::size_type i = 0;
+ std::wstring::size_type length = file_name->size();
+#ifdef U_WCHAR_IS_UTF16
+ // Using |span| method of UnicodeSet might speed things up a bit, but
+ // it's not likely to matter here.
+ const wchar_t* wstr = file_name->data();
+ std::wstring temp;
+ temp.reserve(length);
+ while (i < length) {
+ UChar32 ucs4;
+ std::wstring::size_type prev = i;
+ U16_NEXT(wstr, i, length, ucs4);
+ if (illegal_characters.contains(ucs4)) {
+ temp.push_back(replace_char);
+ } else if (ucs4 < 0x10000) {
+ temp.push_back(ucs4);
+ } else {
+ temp.push_back(wstr[prev]);
+ temp.push_back(wstr[prev + 1]);
+ }
+ }
+ file_name->swap(temp);
+#elif defined(U_WCHAR_IS_UTF32)
+ while (i < length) {
+ if (illegal_characters.contains(wstr[i])) {
+ *file_name[i] = replace_char;
+ }
+ }
+#else
+#error wchar_t* should be either UTF-16 or UTF-32
+#endif
+}
+
+void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) {
+ const wstring::size_type last_dot = file_name->rfind(L'.');
+ wstring result = file_name->substr(0, last_dot);
+ if (!extension.empty() && extension != L".") {
+ if (extension.at(0) != L'.')
+ result.append(L".");
+ result.append(extension);
+ }
+ file_name->swap(result);
+}
+
+wstring GetDirectoryFromPath(const std::wstring& path) {
+ wchar_t path_buffer[MAX_PATH];
+ wchar_t* file_ptr = NULL;
+ if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0)
+ return L"";
+
+ wstring::size_type nc = file_ptr ? file_ptr - path_buffer : path.length();
+ wstring directory(path, 0, nc);
+ TrimTrailingSeparator(&directory);
+ return directory;
+}
+
+int CountFilesCreatedAfter(const std::wstring& path,
+ const FILETIME& comparison_time) {
+ int file_count = 0;
+
+ WIN32_FIND_DATA find_file_data;
+ std::wstring filename_spec = path + L"\\*"; // All files in given dir
+ HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data);
+ if (find_handle != INVALID_HANDLE_VALUE) {
+ do {
+ // Don't count current or parent directories.
+ if ((wcscmp(find_file_data.cFileName, L"..") == 0) ||
+ (wcscmp(find_file_data.cFileName, L".") == 0))
+ continue;
+
+ long result = CompareFileTime(&find_file_data.ftCreationTime,
+ &comparison_time);
+ // File was created after or on comparison time
+ if ((result == 1) || (result == 0))
+ ++file_count;
+ } while (FindNextFile(find_handle, &find_file_data));
+ FindClose(find_handle);
+ }
+
+ return file_count;
+}
+
+bool Delete(const std::wstring& path, bool recursive) {
+ if (path.length() >= MAX_PATH)
+ return false;
+
+ // If we're not recursing use DeleteFile; it should be faster. DeleteFile
+ // fails if passed a directory though, which is why we fall through on
+ // failure to the SHFileOperation.
+ if (!recursive && DeleteFile(path.c_str()) != 0)
+ return true;
+
+ // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+ // so we have to use wcscpy because wcscpy_s writes non-NULLs
+ // into the rest of the buffer.
+ wchar_t double_terminated_path[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ wcscpy(double_terminated_path, path.c_str());
+
+ SHFILEOPSTRUCT file_operation = {0};
+ file_operation.wFunc = FO_DELETE;
+ file_operation.pFrom = double_terminated_path;
+ file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION;
+ if (!recursive)
+ file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+ return (SHFileOperation(&file_operation) == 0);
+}
+
+bool Move(const std::wstring& from_path, const std::wstring& to_path) {
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+ return false;
+ return (MoveFileEx(from_path.c_str(), to_path.c_str(),
+ MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0);
+}
+
+bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) {
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+ return false;
+ return (::CopyFile(from_path.c_str(), to_path.c_str(), false) != 0);
+}
+
+bool ShellCopy(const std::wstring& from_path, const std::wstring& to_path,
+ bool recursive) {
+ // NOTE: I suspect we could support longer paths, but that would involve
+ // analyzing all our usage of files.
+ if (from_path.length() >= MAX_PATH || to_path.length() >= MAX_PATH)
+ return false;
+
+ // SHFILEOPSTRUCT wants the path to be terminated with two NULLs,
+ // so we have to use wcscpy because wcscpy_s writes non-NULLs
+ // into the rest of the buffer.
+ wchar_t double_terminated_path_from[MAX_PATH + 1] = {0};
+ wchar_t double_terminated_path_to[MAX_PATH + 1] = {0};
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ wcscpy(double_terminated_path_from, from_path.c_str());
+#pragma warning(suppress:4996) // don't complain about wcscpy deprecation
+ wcscpy(double_terminated_path_to, to_path.c_str());
+
+ SHFILEOPSTRUCT file_operation = {0};
+ file_operation.wFunc = FO_COPY;
+ file_operation.pFrom = double_terminated_path_from;
+ file_operation.pTo = double_terminated_path_to;
+ file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION |
+ FOF_NOCONFIRMMKDIR;
+ if (!recursive)
+ file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY;
+
+ return (SHFileOperation(&file_operation) == 0);
+}
+
+bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
+ bool recursive) {
+ if (recursive)
+ return ShellCopy(from_path, to_path, true);
+
+ // Instead of creating a new directory, we copy the old one to include the
+ // security information of the folder as part of the copy.
+ if (!PathExists(to_path)) {
+ // Except that Vista fails to do that, and instead do a recursive copy if
+ // the target directory doesn't exist.
+ if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA)
+ CreateDirectory(to_path);
+ else
+ ShellCopy(from_path, to_path, false);
+ }
+
+ std::wstring directory(from_path);
+ AppendToPath(&directory, L"*.*");
+ return ShellCopy(directory, to_path, false);
+}
+
+bool PathExists(const std::wstring& path) {
+ return (GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES);
+}
+
+bool PathIsWritable(const std::wstring& path) {
+ HANDLE dir =
+ CreateFile(path.c_str(), FILE_ADD_FILE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (dir == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(dir);
+ return true;
+}
+
+bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+ LPSYSTEMTIME creation_time) {
+ if (!file_handle)
+ return false;
+
+ FILETIME utc_filetime;
+ if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL))
+ return false;
+
+ FILETIME local_filetime;
+ if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime))
+ return false;
+
+ return !!FileTimeToSystemTime(&local_filetime, creation_time);
+}
+
+bool GetFileCreationLocalTime(const std::wstring& filename,
+ LPSYSTEMTIME creation_time) {
+ ScopedHandle file_handle(
+ CreateFile(filename.c_str(), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+ return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time);
+}
+
+bool ContentsEqual(const std::wstring& filename1,
+ const std::wstring& filename2) {
+ // We open the file in binary format even if they are text files because
+ // we are just comparing that bytes are exactly same in both files and not
+ // doing anything smart with text formatting.
+ std::ifstream file1(filename1.c_str(), std::ios::in | std::ios::binary);
+ std::ifstream file2(filename2.c_str(), std::ios::in | std::ios::binary);
+
+ // Even if both files aren't openable (and thus, in some sense, "equal"),
+ // any unusable file yields a result of "false".
+ if (!file1.is_open() || !file2.is_open())
+ return false;
+
+ const int BUFFER_SIZE = 2056;
+ char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE];
+ do {
+ file1.read(buffer1, BUFFER_SIZE);
+ file2.read(buffer2, BUFFER_SIZE);
+
+ if ((file1.eof() && !file2.eof()) ||
+ (!file1.eof() && file2.eof()) ||
+ (file1.gcount() != file2.gcount()) ||
+ (memcmp(buffer1, buffer2, file1.gcount()))) {
+ file1.close();
+ file2.close();
+ return false;
+ }
+ } while (!file1.eof() && !file2.eof());
+
+ file1.close();
+ file2.close();
+ return true;
+}
+
+bool ReadFileToString(const std::wstring& path, std::string* contents) {
+ FILE* file;
+ errno_t err = _wfopen_s(&file, path.c_str(), L"rbS");
+ if (err != 0)
+ return false;
+
+ char buf[1 << 16];
+ size_t len;
+ while ((len = fread(buf, 1, sizeof(buf), file)) > 0) {
+ contents->append(buf, len);
+ }
+ fclose(file);
+
+ return true;
+}
+
+bool ResolveShortcut(std::wstring* path) {
+ HRESULT result;
+ IShellLink *shell = NULL;
+ bool is_resolved = false;
+
+ // Get pointer to the IShellLink interface
+ result = CoCreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER, IID_IShellLink,
+ reinterpret_cast<LPVOID*>(&shell));
+ if (SUCCEEDED(result)) {
+ IPersistFile *persist = NULL;
+ // Query IShellLink for the IPersistFile interface
+ result = shell->QueryInterface(IID_IPersistFile,
+ reinterpret_cast<LPVOID*>(&persist));
+ if (SUCCEEDED(result)) {
+ WCHAR temp_path[MAX_PATH];
+ // Load the shell link
+ result = persist->Load(path->c_str(), STGM_READ);
+ if (SUCCEEDED(result)) {
+ // Try to find the target of a shortcut
+ result = shell->Resolve(0, SLR_NO_UI);
+ if (SUCCEEDED(result)) {
+ result = shell->GetPath(temp_path, MAX_PATH,
+ NULL, SLGP_UNCPRIORITY);
+ *path = temp_path;
+ is_resolved = true;
+ }
+ }
+ }
+ if (persist)
+ persist->Release();
+ }
+ if (shell)
+ shell->Release();
+
+ return is_resolved;
+}
+
+bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
+ const wchar_t *working_dir, const wchar_t *arguments,
+ const wchar_t *description, const wchar_t *icon,
+ int icon_index) {
+ IShellLink *i_shell_link = NULL;
+ IPersistFile *i_persist_file = NULL;
+
+ // Get pointer to the IShellLink interface
+ HRESULT result = CoCreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER, IID_IShellLink,
+ reinterpret_cast<LPVOID*>(&i_shell_link));
+ if (FAILED(result))
+ return false;
+
+ // Query IShellLink for the IPersistFile interface
+ result = i_shell_link->QueryInterface(IID_IPersistFile,
+ reinterpret_cast<LPVOID*>(&i_persist_file));
+ if (FAILED(result)) {
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (FAILED(i_shell_link->SetPath(source))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (description && FAILED(i_shell_link->SetDescription(description))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ result = i_persist_file->Save(destination, TRUE);
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return SUCCEEDED(result);
+}
+
+
+bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
+ const wchar_t *working_dir, const wchar_t *arguments,
+ const wchar_t *description, const wchar_t *icon,
+ int icon_index) {
+ // Get pointer to the IPersistFile interface and load existing link
+ IShellLink *i_shell_link = NULL;
+ if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER, IID_IShellLink,
+ reinterpret_cast<LPVOID*>(&i_shell_link))))
+ return false;
+
+ IPersistFile *i_persist_file = NULL;
+ if (FAILED(i_shell_link->QueryInterface(
+ IID_IPersistFile, reinterpret_cast<LPVOID*>(&i_persist_file)))) {
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (FAILED(i_persist_file->Load(destination, 0))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (source && FAILED(i_shell_link->SetPath(source))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (arguments && FAILED(i_shell_link->SetArguments(arguments))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (description && FAILED(i_shell_link->SetDescription(description))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) {
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return false;
+ }
+
+ HRESULT result = i_persist_file->Save(destination, TRUE);
+ i_persist_file->Release();
+ i_shell_link->Release();
+ return SUCCEEDED(result);
+}
+
+bool GetTempDir(std::wstring* path) {
+ wchar_t temp_path[MAX_PATH + 1];
+ DWORD path_len = ::GetTempPath(MAX_PATH, temp_path);
+ if (path_len >= MAX_PATH || path_len <= 0)
+ return false;
+ path->assign(temp_path);
+ TrimTrailingSeparator(path);
+ return true;
+}
+
+bool CreateTemporaryFileName(std::wstring* temp_file) {
+ wchar_t temp_name[MAX_PATH + 1];
+ std::wstring temp_path;
+
+ if (!GetTempDir(&temp_path))
+ return false;
+
+ if (!GetTempFileName(temp_path.c_str(), L"", 0, temp_name))
+ return false; // fail!
+
+ DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH);
+ if (path_len > MAX_PATH + 1 || path_len == 0)
+ return false; // fail!
+
+ temp_file->assign(temp_name, path_len);
+ return true;
+}
+
+bool CreateNewTempDirectory(const std::wstring& prefix,
+ std::wstring* new_temp_path) {
+ std::wstring system_temp_dir;
+ if (!GetTempDir(&system_temp_dir))
+ return false;
+
+ std::wstring path_to_create;
+ srand(static_cast<uint32>(time(NULL)));
+
+ int count = 0;
+ while (count < 50) {
+ // Try create a new temporary directory with random generated name. If
+ // the one exists, keep trying another path name until we reach some limit.
+ path_to_create.assign(system_temp_dir);
+ std::wstring new_dir_name;
+ new_dir_name.assign(prefix);
+ new_dir_name.append(IntToWString(rand() % kint16max));
+ file_util::AppendToPath(&path_to_create, new_dir_name);
+
+ if (::CreateDirectory(path_to_create.c_str(), NULL))
+ break;
+ count++;
+ }
+
+ if (count == 50) {
+ return false;
+ }
+
+ new_temp_path->assign(path_to_create);
+ return true;
+}
+
+bool CreateDirectory(const std::wstring& full_path) {
+ int err = SHCreateDirectoryEx(NULL, full_path.c_str(), NULL);
+ return err == ERROR_SUCCESS;
+}
+
+bool GetFileSize(const std::wstring& file_path, int64* file_size) {
+ ScopedHandle file_handle(
+ CreateFile(file_path.c_str(), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
+
+ LARGE_INTEGER win32_file_size = {0};
+ if (!GetFileSizeEx(file_handle, &win32_file_size)) {
+ return false;
+ }
+
+ *file_size = win32_file_size.QuadPart;
+ return true;
+}
+
+int ReadFile(const std::wstring& filename, char* data, int size) {
+ ScopedHandle file(CreateFile(filename.c_str(),
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL));
+ if (file == INVALID_HANDLE_VALUE)
+ return -1;
+
+ int ret_value;
+ DWORD read;
+ if (::ReadFile(file, data, size, &read, NULL) && read == size) {
+ ret_value = static_cast<int>(read);
+ } else {
+ ret_value = -1;
+ }
+
+ return ret_value;
+}
+
+int WriteFile(const std::wstring& filename, const char* data, int size) {
+ ScopedHandle file(CreateFile(filename.c_str(),
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ CREATE_ALWAYS,
+ 0,
+ NULL));
+ if (file == INVALID_HANDLE_VALUE)
+ return -1;
+
+ int ret_value;
+ DWORD written;
+ if (::WriteFile(file, data, size, &written, NULL) && written == size) {
+ ret_value = static_cast<int>(written);
+ } else {
+ ret_value = -1;
+ }
+
+ return ret_value;
+}
+
+FileEnumerator::FileEnumerator(const std::wstring& root_path,
+ bool recursive,
+ FileEnumerator::FILE_TYPE file_type)
+ : recursive_(recursive),
+ file_type_(file_type),
+ is_in_find_op_(false),
+ find_handle_(INVALID_HANDLE_VALUE) {
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::FileEnumerator(const std::wstring& root_path,
+ bool recursive,
+ FileEnumerator::FILE_TYPE file_type,
+ const std::wstring& pattern)
+ : recursive_(recursive),
+ file_type_(file_type),
+ is_in_find_op_(false),
+ pattern_(pattern),
+ find_handle_(INVALID_HANDLE_VALUE) {
+ pending_paths_.push(root_path);
+}
+
+FileEnumerator::~FileEnumerator() {
+ if (find_handle_ != INVALID_HANDLE_VALUE)
+ FindClose(find_handle_);
+}
+
+std::wstring FileEnumerator::Next() {
+ if (!is_in_find_op_) {
+ if (pending_paths_.empty())
+ return std::wstring();
+
+ // The last find FindFirstFile operation is done, prepare a new one.
+ // root_path_ must have the trailing directory character.
+ root_path_ = pending_paths_.top();
+ file_util::AppendToPath(&root_path_, std::wstring());
+ pending_paths_.pop();
+
+ // Start a new find operation.
+ std::wstring src(root_path_);
+
+ if (pattern_.empty())
+ file_util::AppendToPath(&src, L"*"); // No pattern = match everything.
+ else
+ file_util::AppendToPath(&src, pattern_);
+
+ find_handle_ = FindFirstFile(src.c_str(), &find_data_);
+ is_in_find_op_ = true;
+
+ } else {
+ // Search for the next file/directory.
+ if (!FindNextFile(find_handle_, &find_data_)) {
+ FindClose(find_handle_);
+ find_handle_ = INVALID_HANDLE_VALUE;
+ }
+ }
+
+ if (INVALID_HANDLE_VALUE == find_handle_) {
+ is_in_find_op_ = false;
+
+ // This is reached when we have finished a directory and are advancing to
+ // the next one in the queue. We applied the pattern (if any) to the files
+ // in the root search directory, but for those directories which were
+ // matched, we want to enumerate all files inside them. This will happen
+ // when the handle is empty.
+ pattern_.clear();
+
+ return Next();
+ }
+
+ std::wstring cur_file(find_data_.cFileName);
+ // Skip over . and ..
+ if (L"." == cur_file || L".." == cur_file)
+ return Next();
+
+ // Construct the absolute filename.
+ cur_file.insert(0, root_path_);
+
+ if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (recursive_) {
+ // If |cur_file| is a directory, and we are doing recursive searching, add
+ // it to pending_paths_ so we scan it after we finish scanning this
+ // directory.
+ pending_paths_.push(cur_file);
+ return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next();
+ }
+
+ if ((file_type_ & FileEnumerator::DIRECTORIES) == 0)
+ return Next();
+ }
+ return (file_type_ & FileEnumerator::FILES) ? cur_file : Next();
+}
+
+bool RenameFileAndResetSecurityDescriptor(
+ const std::wstring& source_file_path,
+ const std::wstring& target_file_path) {
+ // The MoveFile API does not reset the security descriptor on the target
+ // file. To ensure that the target file gets the correct security descriptor
+ // we create the target file initially in the target path, read its security
+ // descriptor and stamp this descriptor on the target file after the MoveFile
+ // API completes.
+ ScopedHandle temp_file_handle_for_security_desc(
+ CreateFileW(target_file_path.c_str(), GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ, NULL, OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
+ NULL));
+ if (!temp_file_handle_for_security_desc.IsValid())
+ return false;
+
+ // Check how much we should allocate for the security descriptor.
+ unsigned long security_descriptor_size_in_bytes = 0;
+ GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION, NULL, 0,
+ &security_descriptor_size_in_bytes);
+ if (ERROR_INSUFFICIENT_BUFFER != GetLastError() ||
+ security_descriptor_size_in_bytes == 0)
+ return false;
+
+ scoped_array<char> security_descriptor(
+ new char[security_descriptor_size_in_bytes]);
+
+ if (!GetFileSecurity(target_file_path.c_str(), DACL_SECURITY_INFORMATION,
+ security_descriptor.get(),
+ security_descriptor_size_in_bytes,
+ &security_descriptor_size_in_bytes)) {
+ return false;
+ }
+
+ temp_file_handle_for_security_desc.Set(INVALID_HANDLE_VALUE);
+
+ if (!MoveFileEx(source_file_path.c_str(), target_file_path.c_str(),
+ MOVEFILE_COPY_ALLOWED)) {
+ return false;
+ }
+
+ return !!SetFileSecurity(target_file_path.c_str(),
+ DACL_SECURITY_INFORMATION,
+ security_descriptor.get());
+}
+
+} // namespace
diff --git a/base/file_util.h b/base/file_util.h
new file mode 100644
index 0000000..9783e93
--- /dev/null
+++ b/base/file_util.h
@@ -0,0 +1,302 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains utility functions for dealing with the local
+// filesystem.
+
+#ifndef BASE_FILE_UTIL_H__
+#define BASE_FILE_UTIL_H__
+
+#include <windows.h>
+#include <stack>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace file_util {
+
+//-----------------------------------------------------------------------------
+// Constants
+
+extern const wchar_t kPathSeparator;
+
+
+//-----------------------------------------------------------------------------
+// Functions that operate purely on a path string w/o touching the filesystem:
+
+// Returns true if the given path ends with a path separator character.
+bool EndsWithSeparator(std::wstring* path);
+
+// Modifies a string by trimming all trailing separators from the end.
+void TrimTrailingSeparator(std::wstring* dir);
+
+// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not
+// refer to a file.
+// If 'dir' is a root directory, return without change.
+void UpOneDirectory(std::wstring* dir);
+
+// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not
+// refer to a file.
+// If 'dir' is a root directory, the result becomes empty string.
+void UpOneDirectoryOrEmpty(std::wstring* dir);
+
+// Strips the filename component from the end of 'path'.
+void TrimFilename(std::wstring* path);
+
+// Returns the filename portion of 'path', without any leading \'s or /'s.
+std::wstring GetFilenameFromPath(const std::wstring& path);
+
+// Returns "jpg" for path "C:\pics\jojo.jpg", or an empty string if
+// the file has no extension.
+std::wstring GetFileExtensionFromPath(const std::wstring& path);
+
+// Returns the directory component of a path, without the trailing
+// path separator, or an empty string on error. The function does not
+// check for the existence of the path, so if it is passed a directory
+// without the trailing \, it will interpret the last component of the
+// path as a file and chomp it. This does not support relative paths.
+// Examples:
+// path == "C:\pics\jojo.jpg", returns "C:\pics"
+// path == "C:\Windows\system32\", returns "C:\Windows\system32"
+// path == "C:\Windows\system32", returns "C:\Windows"
+std::wstring GetDirectoryFromPath(const std::wstring& path);
+
+// Appends new_ending to path, adding a separator between the two if necessary.
+void AppendToPath(std::wstring* path, const std::wstring& new_ending);
+
+// Inserts |suffix| after the file name portion of |path| but before the
+// extension.
+// Examples:
+// path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg"
+// path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg"
+// path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)"
+// path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)"
+void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix);
+
+// Replaces characters in 'file_name' that are illegal for file names with
+// 'replace_char'. 'file_name' must not be a full or relative path, but just the
+// file name component. Any leading or trailing whitespace in 'file_name' is
+// removed.
+// Example:
+// file_name == "bad:file*name?.txt", changed to: "bad-file-name-.txt" when
+// 'replace_char' is '-'.
+void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char);
+
+// Replaces the extension of |file_name| with |extension|. If |file_name|
+// does not have an extension, them |extension| is added. If |extention| is
+// empty, then the extension is removed from |file_name|.
+void ReplaceExtension(std::wstring* file_name, const std::wstring& extension);
+
+//-----------------------------------------------------------------------------
+// Functions that involve filesystem access or modification:
+
+// Returns the number of files matching the current path that were
+// created on or after the given FILETIME. Doesn't count ".." or ".".
+// Filetime is UTC filetime, not LocalFiletime.
+int CountFilesCreatedAfter(const std::wstring& path,
+ const FILETIME& file_time);
+
+// Deletes the given path, whether it's a file or a directory.
+// If it's a directory, it's perfectly happy to delete all of the
+// directory's contents. Passing true to recursive deletes
+// subdirectories and their contents as well.
+// Returns true if successful, false otherwise.
+//
+// WARNING: USING THIS WITH recursive==true IS EQUIVALENT
+// TO "rm -rf", SO USE WITH CAUTION.
+bool Delete(const std::wstring& path, bool recursive);
+
+// Moves the given path, whether it's a file or a directory.
+// Returns true if successful, false otherwise.
+bool Move(const std::wstring& from_path, const std::wstring& to_path);
+
+// Copies a single file. Use CopyDirectory to copy directories.
+bool CopyFile(const std::wstring& from_path, const std::wstring& to_path);
+
+// Copies the given path, and optionally all subdirectories and their contents
+// as well.
+// If there are files existing under to_path, always overwrite.
+// Returns true if successful, false otherwise.
+// Dont't use wildcards on the names, it may stop working without notice.
+//
+// If you only need to copy a file use CopyFile, it's faster.
+bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path,
+ bool recursive);
+
+// Returns true if the given path exists on the local filesystem,
+// false otherwise.
+bool PathExists(const std::wstring& path);
+
+// Returns true if the given path is writable by the user, false otherwise.
+bool PathIsWritable(const std::wstring& path);
+
+// Gets the creation time of the given file (expressed in the local timezone),
+// and returns it via the creation_time parameter. Returns true if successful,
+// false otherwise.
+bool GetFileCreationLocalTime(const std::wstring& filename,
+ LPSYSTEMTIME creation_time);
+
+// Same as above, but takes a previously-opened file handle instead of a name.
+bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle,
+ LPSYSTEMTIME creation_time);
+
+// Returns true if the contents of the two files given are equal, false
+// otherwise. If either file can't be read, returns false.
+bool ContentsEqual(const std::wstring& filename1,
+ const std::wstring& filename2);
+
+// Read the file at |path| into |contents|, returning true on success.
+// Useful for unit tests.
+bool ReadFileToString(const std::wstring& path, std::string* contents);
+
+// Resolve Windows shortcut (.LNK file)
+// Argument path specifies a valid LNK file. On success, return true and put
+// the URL into path. If path is a invalid .LNK file, return false.
+bool ResolveShortcut(std::wstring* path);
+
+// Create a Windows shortcut (.LNK file)
+// This method creates a shortcut link using the information given. Ensure
+// you have initialized COM before calling into this function. 'source'
+// and 'destination' parameters are required, everything else can be NULL.
+// 'source' is the existing file, 'destination' is the new link file to be
+// created; for best resoults pass the filename with the .lnk extension.
+// The 'icon' can specify a dll or exe in which case the icon index is the
+// resource id.
+// Note that if the shortcut exists it will overwrite it.
+bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination,
+ const wchar_t *working_dir, const wchar_t *arguments,
+ const wchar_t *description, const wchar_t *icon,
+ int icon_index);
+
+// Update a Windows shortcut (.LNK file). This method assumes the shortcut
+// link already exists (otherwise false is returned). Ensure you have
+// initialized COM before calling into this function. Only 'destination'
+// parameter is required, everything else can be NULL (but if everthing else
+// is NULL no changes are made to the shortcut). 'destination' is the link
+// file to be updated. For best results pass the filename with the .lnk
+// extension.
+bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination,
+ const wchar_t *working_dir, const wchar_t *arguments,
+ const wchar_t *description, const wchar_t *icon,
+ int icon_index);
+
+// Get the temporary directory provided by the system.
+bool GetTempDir(std::wstring* path);
+
+// Creates a temporary file name, but does it not create the file. It accesses
+// the disk to do this, however. The full path is placed in 'temp_file', and the
+// function returns true if was successful in creating the file name.
+bool CreateTemporaryFileName(std::wstring* temp_file);
+
+// Create a new directory under TempPath. If prefix is provided, the new
+// directory name is in the format of prefixyyyy.
+// If success, return true and output the full path of the directory created.
+bool CreateNewTempDirectory(const std::wstring& prefix,
+ std::wstring* new_temp_path);
+
+// Creates a directory, as well as creating any parent directories, if they
+// don't exist. Returns 'true' on successful creation.
+bool CreateDirectory(const std::wstring& full_path);
+
+// Returns the file size. Returns true on success.
+bool GetFileSize(const std::wstring& file_path, int64* file_size);
+
+// Reads the given number of bytes from the file into the buffer. Returns
+// the number of read bytes, or -1 on error.
+int ReadFile(const std::wstring& filename, char* data, int size);
+
+// Writes the given buffer into the file, overwriting any data that was
+// previously there. Returns the number of bytes written, or -1 on error.
+int WriteFile(const std::wstring& filename, const char* data, int size);
+
+// A class for enumerating the files in a provided path. The order of the
+// results is not guaranteed.
+//
+// DO NOT USE FROM THE MAIN THREAD of your application unless it is a test
+// program where latency does not matter. This class is blocking.
+class FileEnumerator {
+ public:
+ enum FILE_TYPE {
+ FILES = 0x1,
+ DIRECTORIES = 0x2,
+ FILES_AND_DIRECTORIES = 0x3
+ };
+
+ // |root_path| is the starting directory to search for. It may or may not end
+ // in a slash.
+ //
+ // If |recursive| is true, this will enumerate all matches in any
+ // subdirectories matched as well. It does a breadth-first search, so all
+ // files in one directory will be returned before any files in a
+ // subdirectory.
+ //
+ // The last parameter is an optional pattern for which files to match. This
+ // works like a Windows file pattern. For example, "*.txt" or "Foo???.doc".
+ // If unspecified, this will match all files.
+ FileEnumerator(const std::wstring& root_path,
+ bool recursive,
+ FileEnumerator::FILE_TYPE file_type);
+ FileEnumerator(const std::wstring& root_path,
+ bool recursive,
+ FileEnumerator::FILE_TYPE file_type,
+ const std::wstring& pattern);
+ ~FileEnumerator();
+
+ // Returns an empty string if there are no more results.
+ std::wstring Next();
+
+ private:
+ std::wstring root_path_;
+ bool recursive_;
+ FILE_TYPE file_type_;
+ std::wstring pattern_; // Empty when we want to find everything.
+
+ // Set to true when there is a find operation open. This way, we can lazily
+ // start the operations when the caller calls Next().
+ bool is_in_find_op_;
+
+ // A stack that keeps track of which subdirectories we still need to
+ // enumerate in the breadth-first search.
+ std::stack<std::wstring> pending_paths_;
+
+ WIN32_FIND_DATA find_data_;
+ HANDLE find_handle_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FileEnumerator);
+};
+
+// Renames a file using the MoveFileEx API and ensures that the target file gets
+// the correct security descriptor in the new path.
+bool RenameFileAndResetSecurityDescriptor(
+ const std::wstring& source_file_path,
+ const std::wstring& target_file_path);
+
+} // namespace file_util
+
+#endif // BASE_FILE_UTIL_H__
diff --git a/base/file_util_unittest.cc b/base/file_util_unittest.cc
new file mode 100644
index 0000000..f82a894
--- /dev/null
+++ b/base/file_util_unittest.cc
@@ -0,0 +1,777 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <set>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include <fstream>
+#include <iostream>
+
+#include "base/base_paths.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FileUtilTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Name a subdirectory of the temp directory.
+ ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_));
+ file_util::AppendToPath(&test_dir_, L"FileUtilTest");
+
+ // Create a fresh, empty copy of this directory.
+ file_util::Delete(test_dir_, true);
+ CreateDirectory(test_dir_.c_str(), NULL);
+ }
+ virtual void TearDown() {
+ // Clean up test directory
+ ASSERT_TRUE(file_util::Delete(test_dir_, false));
+ ASSERT_FALSE(file_util::PathExists(test_dir_));
+ }
+
+ // the path to temporary directory used to contain the test operations
+ std::wstring test_dir_;
+};
+
+// Collects all the results from the given file enumerator, and provides an
+// interface to query whether a given file is present.
+class FindResultCollector {
+ public:
+ FindResultCollector(file_util::FileEnumerator& enumerator) {
+ std::wstring cur_file;
+ while (!(cur_file = enumerator.Next()).empty()) {
+ // The file should not be returned twice.
+ EXPECT_TRUE(files_.end() == files_.find(cur_file))
+ << "Same file returned twice";
+
+ // Save for later.
+ files_.insert(cur_file);
+ }
+ }
+
+ // Returns true if the enumerator found the file.
+ bool HasFile(const std::wstring& file) const {
+ return files_.find(file) != files_.end();
+ }
+
+ private:
+ std::set<std::wstring> files_;
+};
+
+// Simple function to dump some text into a new file.
+void CreateTextFile(const std::wstring& filename,
+ const std::wstring& contents) {
+ std::ofstream file;
+ file.open(filename.c_str());
+ ASSERT_TRUE(file.is_open());
+ file << contents;
+ file.close();
+}
+
+// Simple function to take out some text from a file.
+std::wstring ReadTextFile(const std::wstring& filename) {
+ WCHAR contents[64];
+ std::wifstream file;
+ file.open(filename.c_str());
+ EXPECT_TRUE(file.is_open());
+ file.getline(contents, 64);
+ file.close();
+ return std::wstring(contents);
+}
+
+uint64 FileTimeAsUint64(const FILETIME& ft) {
+ ULARGE_INTEGER u;
+ u.LowPart = ft.dwLowDateTime;
+ u.HighPart = ft.dwHighDateTime;
+ return u.QuadPart;
+}
+
+const struct append_case {
+ const wchar_t* path;
+ const wchar_t* ending;
+ const wchar_t* result;
+} append_cases[] = {
+ {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"\\"},
+};
+
+} // namespace
+
+TEST_F(FileUtilTest, AppendToPath) {
+ for (int i = 0; i < arraysize(append_cases); ++i) {
+ const append_case& value = append_cases[i];
+ std::wstring result = value.path;
+ file_util::AppendToPath(&result, value.ending);
+ EXPECT_EQ(value.result, result);
+ }
+
+#ifdef NDEBUG
+ file_util::AppendToPath(NULL, L"path"); // asserts in debug mode
+#endif
+}
+
+static const struct InsertBeforeExtensionCase {
+ std::wstring path;
+ std::wstring suffix;
+ std::wstring result;
+} kInsertBeforeExtension[] = {
+ {L"", L"", L""},
+ {L"", L"txt", L"txt"},
+ {L".", L"txt", L"txt."},
+ {L".", L"", L"."},
+ {L"foo.dll", L"txt", L"footxt.dll"},
+ {L"foo.dll", L".txt", L"foo.txt.dll"},
+ {L"foo", L"txt", L"footxt"},
+ {L"foo", L".txt", L"foo.txt"},
+ {L"foo.baz.dll", L"txt", L"foo.baztxt.dll"},
+ {L"foo.baz.dll", L".txt", L"foo.baz.txt.dll"},
+ {L"foo.dll", L"", L"foo.dll"},
+ {L"foo.dll", L".", L"foo..dll"},
+ {L"foo", L"", L"foo"},
+ {L"foo", L".", L"foo."},
+ {L"foo.baz.dll", L"", L"foo.baz.dll"},
+ {L"foo.baz.dll", L".", L"foo.baz..dll"},
+ {L"\\", L"", L"\\"},
+ {L"\\", L"txt", L"\\txt"},
+ {L"\\.", L"txt", L"\\txt."},
+ {L"\\.", L"", L"\\."},
+ {L"C:\\bar\\foo.dll", L"txt", L"C:\\bar\\footxt.dll"},
+ {L"C:\\bar.baz\\foodll", L"txt", L"C:\\bar.baz\\foodlltxt"},
+ {L"C:\\bar.baz\\foo.dll", L"txt", L"C:\\bar.baz\\footxt.dll"},
+ {L"C:\\bar.baz\\foo.dll.exe", L"txt", L"C:\\bar.baz\\foo.dlltxt.exe"},
+ {L"C:\\bar.baz\\foo", L"", L"C:\\bar.baz\\foo"},
+ {L"C:\\bar.baz\\foo.exe", L"", L"C:\\bar.baz\\foo.exe"},
+ {L"C:\\bar.baz\\foo.dll.exe", L"", L"C:\\bar.baz\\foo.dll.exe"},
+ {L"C:\\bar\\baz\\foo.exe", L" (1)", L"C:\\bar\\baz\\foo (1).exe"},
+};
+
+TEST_F(FileUtilTest, InsertBeforeExtensionTest) {
+ for (int i = 0; i < arraysize(kInsertBeforeExtension); ++i) {
+ std::wstring path(kInsertBeforeExtension[i].path);
+ file_util::InsertBeforeExtension(&path, kInsertBeforeExtension[i].suffix);
+ EXPECT_EQ(path, kInsertBeforeExtension[i].result);
+ }
+}
+
+static const struct filename_case {
+ const wchar_t* path;
+ const wchar_t* filename;
+} filename_cases[] = {
+ {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"},
+};
+
+TEST_F(FileUtilTest, GetFilenameFromPath) {
+ for (int i = 0; i < arraysize(filename_cases); ++i) {
+ const filename_case& value = filename_cases[i];
+ std::wstring result = file_util::GetFilenameFromPath(value.path);
+ EXPECT_EQ(value.filename, result);
+ }
+}
+
+// Test finding the file type from a path name
+static const struct extension_case {
+ const wchar_t* path;
+ const wchar_t* extension;
+} extension_cases[] = {
+ {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"},
+};
+
+TEST_F(FileUtilTest, GetFileExtensionFromPath) {
+ for (int i = 0; i < arraysize(extension_cases); ++i) {
+ const extension_case& ext = extension_cases[i];
+ const std::wstring fext = file_util::GetFileExtensionFromPath(ext.path);
+ EXPECT_EQ(ext.extension, fext);
+ }
+}
+
+// Test finding the directory component of a path
+static const struct dir_case {
+ const wchar_t* full_path;
+ const wchar_t* directory;
+} dir_cases[] = {
+ {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:"},
+};
+
+TEST_F(FileUtilTest, GetDirectoryFromPath) {
+ for (int i = 0; i < arraysize(dir_cases); ++i) {
+ const dir_case& dir = dir_cases[i];
+ const std::wstring parent =
+ file_util::GetDirectoryFromPath(dir.full_path);
+ EXPECT_EQ(dir.directory, parent);
+ }
+}
+
+TEST_F(FileUtilTest, CountFilesCreatedAfter) {
+ // Create old file (that we don't want to count)
+ std::wstring old_file_name = test_dir_;
+ file_util::AppendToPath(&old_file_name, L"Old File.txt");
+ CreateTextFile(old_file_name, L"Just call me Mr. Creakybits");
+
+ // Age to perfection
+ Sleep(100);
+
+ // Establish our cutoff time
+ FILETIME test_start_time;
+ GetSystemTimeAsFileTime(&test_start_time);
+ EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+
+ // Create a new file (that we do want to count)
+ std::wstring new_file_name = test_dir_;
+ file_util::AppendToPath(&new_file_name, L"New File.txt");
+ CreateTextFile(new_file_name, L"Waaaaaaaaaaaaaah.");
+
+ // We should see only the new file.
+ EXPECT_EQ(1, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+
+ // Delete new file, we should see no files after cutoff now
+ EXPECT_TRUE(file_util::Delete(new_file_name, false));
+ EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, test_start_time));
+}
+
+// Tests that the Delete function works as expected, especially
+// the recursion flag. Also coincidentally tests PathExists.
+TEST_F(FileUtilTest, Delete) {
+ // Create a file
+ std::wstring file_name = test_dir_;
+ file_util::AppendToPath(&file_name, L"Test File.txt");
+ CreateTextFile(file_name, L"I'm cannon fodder.");
+
+ ASSERT_TRUE(file_util::PathExists(file_name));
+
+ std::wstring subdir_path = test_dir_;
+ file_util::AppendToPath(&subdir_path, L"Subdirectory");
+ CreateDirectory(subdir_path.c_str(), NULL);
+
+ ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+ std::wstring directory_contents = test_dir_;
+ file_util::AppendToPath(&directory_contents, L"*");
+
+ // Delete non-recursively and check that only the file is deleted
+ ASSERT_TRUE(file_util::Delete(directory_contents, false));
+ ASSERT_FALSE(file_util::PathExists(file_name));
+ ASSERT_TRUE(file_util::PathExists(subdir_path));
+
+ // Delete recursively and make sure all contents are deleted
+ ASSERT_TRUE(file_util::Delete(directory_contents, true));
+ ASSERT_FALSE(file_util::PathExists(file_name));
+ ASSERT_FALSE(file_util::PathExists(subdir_path));
+}
+
+TEST_F(FileUtilTest, Move) {
+ // Create a directory
+ std::wstring dir_name_from(test_dir_);
+ file_util::AppendToPath(&dir_name_from, L"Move_From_Subdir");
+ CreateDirectory(dir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ std::wstring file_name_from(dir_name_from);
+ file_util::AppendToPath(&file_name_from, L"Move_Test_File.txt");
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Move the directory
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Move_To_Subdir");
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"Move_Test_File.txt");
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::Move(dir_name_from, dir_name_to));
+
+ // Check everything has been moved.
+ EXPECT_FALSE(file_util::PathExists(dir_name_from));
+ EXPECT_FALSE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectoryRecursively) {
+ // Create a directory.
+ std::wstring dir_name_from(test_dir_);
+ file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+ CreateDirectory(dir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ std::wstring file_name_from(dir_name_from);
+ file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ std::wstring subdir_name_from(dir_name_from);
+ file_util::AppendToPath(&subdir_name_from, L"Subdir");
+ CreateDirectory(subdir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ std::wstring file_name2_from(subdir_name_from);
+ file_util::AppendToPath(&file_name2_from, L"Copy_Test_File.txt");
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory recursively.
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"Copy_Test_File.txt");
+ std::wstring subdir_name_to(dir_name_to);
+ file_util::AppendToPath(&subdir_name_to, L"Subdir");
+ std::wstring file_name2_to(subdir_name_to);
+ file_util::AppendToPath(&file_name2_to, L"Copy_Test_File.txt");
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, true));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name2_to));
+}
+
+TEST_F(FileUtilTest, CopyDirectory) {
+ // Create a directory.
+ std::wstring dir_name_from(test_dir_);
+ file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+ CreateDirectory(dir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory.
+ std::wstring file_name_from(dir_name_from);
+ file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+ CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Create a subdirectory.
+ std::wstring subdir_name_from(dir_name_from);
+ file_util::AppendToPath(&subdir_name_from, L"Subdir");
+ CreateDirectory(subdir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(subdir_name_from));
+
+ // Create a file under the subdirectory.
+ std::wstring file_name2_from(subdir_name_from);
+ file_util::AppendToPath(&file_name2_from, L"Copy_Test_File.txt");
+ CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle");
+ ASSERT_TRUE(file_util::PathExists(file_name2_from));
+
+ // Copy the directory not recursively.
+ std::wstring dir_name_to(test_dir_);
+ file_util::AppendToPath(&dir_name_to, L"Copy_To_Subdir");
+ std::wstring file_name_to(dir_name_to);
+ file_util::AppendToPath(&file_name_to, L"Copy_Test_File.txt");
+ std::wstring subdir_name_to(dir_name_to);
+ file_util::AppendToPath(&subdir_name_to, L"Subdir");
+
+ ASSERT_FALSE(file_util::PathExists(dir_name_to));
+
+ EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, false));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(dir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(subdir_name_from));
+ EXPECT_TRUE(file_util::PathExists(file_name2_from));
+ EXPECT_TRUE(file_util::PathExists(dir_name_to));
+ EXPECT_TRUE(file_util::PathExists(file_name_to));
+ EXPECT_FALSE(file_util::PathExists(subdir_name_to));
+}
+
+TEST_F(FileUtilTest, CopyFile) {
+ // Create a directory
+ std::wstring dir_name_from(test_dir_);
+ file_util::AppendToPath(&dir_name_from, L"Copy_From_Subdir");
+ CreateDirectory(dir_name_from.c_str(), NULL);
+ ASSERT_TRUE(file_util::PathExists(dir_name_from));
+
+ // Create a file under the directory
+ std::wstring file_name_from(dir_name_from);
+ file_util::AppendToPath(&file_name_from, L"Copy_Test_File.txt");
+ const std::wstring file_contents(L"Gooooooooooooooooooooogle");
+ CreateTextFile(file_name_from, file_contents);
+ ASSERT_TRUE(file_util::PathExists(file_name_from));
+
+ // Copy the file.
+ std::wstring dest_file(dir_name_from);
+ file_util::AppendToPath(&dest_file, L"DestFile.txt");
+ ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file));
+
+ // Check everything has been copied.
+ EXPECT_TRUE(file_util::PathExists(file_name_from));
+ EXPECT_TRUE(file_util::PathExists(dest_file));
+ const std::wstring read_contents = ReadTextFile(dest_file);
+ EXPECT_EQ(file_contents, read_contents);
+}
+
+TEST_F(FileUtilTest, GetFileCreationLocalTime) {
+ std::wstring file_name = test_dir_;
+ file_util::AppendToPath(&file_name, L"Test File.txt");
+
+ SYSTEMTIME start_time;
+ GetLocalTime(&start_time);
+ Sleep(100);
+ CreateTextFile(file_name, L"New file!");
+ Sleep(100);
+ SYSTEMTIME end_time;
+ GetLocalTime(&end_time);
+
+ SYSTEMTIME file_creation_time;
+ file_util::GetFileCreationLocalTime(file_name, &file_creation_time);
+
+ FILETIME start_filetime;
+ SystemTimeToFileTime(&start_time, &start_filetime);
+ FILETIME end_filetime;
+ SystemTimeToFileTime(&end_time, &end_filetime);
+ FILETIME file_creation_filetime;
+ SystemTimeToFileTime(&file_creation_time, &file_creation_filetime);
+
+ EXPECT_EQ(-1, CompareFileTime(&start_filetime, &file_creation_filetime)) <<
+ "start time: " << FileTimeAsUint64(start_filetime) << ", " <<
+ "creation time: " << FileTimeAsUint64(file_creation_filetime);
+
+ EXPECT_EQ(-1, CompareFileTime(&file_creation_filetime, &end_filetime)) <<
+ "creation time: " << FileTimeAsUint64(file_creation_filetime) << ", " <<
+ "end time: " << FileTimeAsUint64(end_filetime);
+
+ ASSERT_TRUE(DeleteFile(file_name.c_str()));
+}
+
+typedef testing::Test ReadOnlyFileUtilTest;
+
+TEST(ReadOnlyFileUtilTest, ContentsEqual) {
+ std::wstring data_dir;
+ ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir));
+ file_util::AppendToPath(&data_dir, L"base");
+ file_util::AppendToPath(&data_dir, L"data");
+ file_util::AppendToPath(&data_dir, L"file_util_unittest");
+ ASSERT_TRUE(file_util::PathExists(data_dir));
+
+ std::wstring original_file = data_dir;
+ file_util::AppendToPath(&original_file, L"original.txt");
+ std::wstring same_file = data_dir;
+ file_util::AppendToPath(&same_file, L"same.txt");
+ std::wstring same_length_file = data_dir;
+ file_util::AppendToPath(&same_length_file, L"same_length.txt");
+ std::wstring different_file = data_dir;
+ file_util::AppendToPath(&different_file, L"different.txt");
+ std::wstring different_first_file = data_dir;
+ file_util::AppendToPath(&different_first_file, L"different_first.txt");
+ std::wstring different_last_file = data_dir;
+ file_util::AppendToPath(&different_last_file, L"different_last.txt");
+ std::wstring empty1_file = data_dir;
+ file_util::AppendToPath(&empty1_file, L"empty1.txt");
+ std::wstring empty2_file = data_dir;
+ file_util::AppendToPath(&empty2_file, L"empty2.txt");
+ std::wstring shortened_file = data_dir;
+ file_util::AppendToPath(&shortened_file, L"shortened.txt");
+ std::wstring binary_file = data_dir;
+ file_util::AppendToPath(&binary_file, L"binary_file.bin");
+ std::wstring binary_file_same = data_dir;
+ file_util::AppendToPath(&binary_file_same, L"binary_file_same.bin");
+ std::wstring binary_file_diff = data_dir;
+ file_util::AppendToPath(&binary_file_diff, L"binary_file_diff.bin");
+
+ EXPECT_TRUE(file_util::ContentsEqual(original_file, original_file));
+ EXPECT_TRUE(file_util::ContentsEqual(original_file, same_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, same_length_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_file));
+ EXPECT_FALSE(file_util::ContentsEqual(L"bogusname", L"bogusname"));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_first_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, different_last_file));
+ EXPECT_TRUE(file_util::ContentsEqual(empty1_file, empty2_file));
+ EXPECT_FALSE(file_util::ContentsEqual(original_file, shortened_file));
+ EXPECT_FALSE(file_util::ContentsEqual(shortened_file, original_file));
+ EXPECT_TRUE(file_util::ContentsEqual(binary_file, binary_file_same));
+ EXPECT_FALSE(file_util::ContentsEqual(binary_file, binary_file_diff));
+}
+
+TEST_F(FileUtilTest, ResolveShortcutTest) {
+ std::wstring target_file = test_dir_;
+ file_util::AppendToPath(&target_file, L"Target.txt");
+ CreateTextFile(target_file, L"This is the target.");
+
+ std::wstring link_file = test_dir_;
+ file_util::AppendToPath(&link_file, L"Link.lnk");
+
+ HRESULT result;
+ IShellLink *shell = NULL;
+ IPersistFile *persist = NULL;
+
+ CoInitialize(NULL);
+ // Temporarily create a shortcut for test
+ result = CoCreateInstance(CLSID_ShellLink, NULL,
+ CLSCTX_INPROC_SERVER, IID_IShellLink,
+ reinterpret_cast<LPVOID*>(&shell));
+ EXPECT_TRUE(SUCCEEDED(result));
+ result = shell->QueryInterface(IID_IPersistFile,
+ reinterpret_cast<LPVOID*>(&persist));
+ EXPECT_TRUE(SUCCEEDED(result));
+ result = shell->SetPath(target_file.c_str());
+ EXPECT_TRUE(SUCCEEDED(result));
+ result = shell->SetDescription(L"ResolveShortcutTest");
+ EXPECT_TRUE(SUCCEEDED(result));
+ result = persist->Save(link_file.c_str(), TRUE);
+ EXPECT_TRUE(SUCCEEDED(result));
+ if (persist)
+ persist->Release();
+ if (shell)
+ shell->Release();
+
+ bool is_solved;
+ is_solved = file_util::ResolveShortcut(&link_file);
+ EXPECT_TRUE(is_solved);
+ std::wstring contents;
+ contents = ReadTextFile(link_file);
+ EXPECT_EQ(L"This is the target.", contents);
+
+ // Cleanning
+ DeleteFile(target_file.c_str());
+ DeleteFile(link_file.c_str());
+ CoUninitialize();
+}
+
+TEST_F(FileUtilTest, CreateShortcutTest) {
+ const wchar_t file_contents[] = L"This is another target.";
+ std::wstring target_file = test_dir_;
+ file_util::AppendToPath(&target_file, L"Target1.txt");
+ CreateTextFile(target_file, file_contents);
+
+ std::wstring link_file = test_dir_;
+ file_util::AppendToPath(&link_file, L"Link1.lnk");
+
+ CoInitialize(NULL);
+ EXPECT_TRUE(file_util::CreateShortcutLink(target_file.c_str(),
+ link_file.c_str(),
+ NULL, NULL, NULL, NULL, 0));
+ std::wstring resolved_name = link_file;
+ EXPECT_TRUE(file_util::ResolveShortcut(&resolved_name));
+ std::wstring read_contents = ReadTextFile(resolved_name);
+ EXPECT_EQ(file_contents, read_contents);
+
+ DeleteFile(target_file.c_str());
+ DeleteFile(link_file.c_str());
+ CoUninitialize();
+}
+
+TEST_F(FileUtilTest, CreateTemporaryFileNameTest) {
+ std::wstring temp_file;
+ file_util::CreateTemporaryFileName(&temp_file);
+ EXPECT_EQ(file_util::PathExists(temp_file), true);
+}
+
+TEST_F(FileUtilTest, CreateNewTempDirectoryTest) {
+ std::wstring temp_dir;
+ file_util::CreateNewTempDirectory(std::wstring(), &temp_dir);
+ EXPECT_EQ(file_util::PathExists(temp_dir), true);
+}
+
+TEST_F(FileUtilTest, CreateDirectoryTest) {
+ std::wstring test_root = test_dir_;
+ file_util::AppendToPath(&test_root, L"create_directory_test");
+ std::wstring test_path(test_root);
+ file_util::AppendToPath(&test_path, L"dir\\tree\\likely\\doesnt\\exist\\");
+
+ EXPECT_EQ(file_util::PathExists(test_path), false);
+ EXPECT_EQ(file_util::CreateDirectory(test_path), true);
+ EXPECT_EQ(file_util::PathExists(test_path), true);
+ EXPECT_EQ(file_util::Delete(test_root, true), true);
+ EXPECT_EQ(file_util::PathExists(test_root), false);
+ EXPECT_EQ(file_util::PathExists(test_path), false);
+}
+
+static const struct {
+ std::wstring bad_name;
+ std::wstring good_name;
+} kIllegalCharacterCases[] = {
+ {L"bad*file:name?.jpg", L"bad-file-name-.jpg"},
+ {L"**********::::.txt", L"--------------.txt"},
+ {L"bad*file\\name.jpg", L"bad-file-name.jpg"},
+ // We can't use UCNs (universal character names) for C0/C1 characters and
+ // U+007F, but \x escape is interpreted by MSVC and gcc as we intend.
+ {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"},
+ {L"\t bad*file\\name/.jpg ", L"bad-file-name-.jpg"},
+ {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"},
+ {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"},
+ {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"},
+ {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"},
+ {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"},
+ // Unassigned codepoints are ok.
+ {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"},
+};
+
+TEST_F(FileUtilTest, ReplaceIllegalCharactersTest) {
+ for (int i = 0; i < arraysize(kIllegalCharacterCases); ++i) {
+ std::wstring bad_name(kIllegalCharacterCases[i].bad_name);
+ file_util::ReplaceIllegalCharacters(&bad_name, L'-');
+ EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name);
+ }
+}
+
+static const struct ReplaceExtensionCase {
+ std::wstring file_name;
+ std::wstring extension;
+ std::wstring result;
+} kReplaceExtension[] = {
+ {L"", L"", L""},
+ {L"", L"txt", L".txt"},
+ {L".", L"txt", L".txt"},
+ {L".", L"", L""},
+ {L"foo.dll", L"txt", L"foo.txt"},
+ {L"foo.dll", L".txt", L"foo.txt"},
+ {L"foo", L"txt", L"foo.txt"},
+ {L"foo", L".txt", L"foo.txt"},
+ {L"foo.baz.dll", L"txt", L"foo.baz.txt"},
+ {L"foo.baz.dll", L".txt", L"foo.baz.txt"},
+ {L"foo.dll", L"", L"foo"},
+ {L"foo.dll", L".", L"foo"},
+ {L"foo", L"", L"foo"},
+ {L"foo", L".", L"foo"},
+ {L"foo.baz.dll", L"", L"foo.baz"},
+ {L"foo.baz.dll", L".", L"foo.baz"},
+};
+
+TEST_F(FileUtilTest, ReplaceExtensionTest) {
+ for (int i = 0; i < arraysize(kReplaceExtension); ++i) {
+ std::wstring file_name(kReplaceExtension[i].file_name);
+ file_util::ReplaceExtension(&file_name, kReplaceExtension[i].extension);
+ EXPECT_EQ(file_name, kReplaceExtension[i].result);
+ }
+}
+
+TEST_F(FileUtilTest, FileEnumeratorTest) {
+ // Test an empty directory.
+ file_util::FileEnumerator f0(test_dir_, true,
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+ EXPECT_EQ(f0.Next(), L"");
+ EXPECT_EQ(f0.Next(), L"");
+
+ // Populate the test dir.
+ file_util::CreateDirectory(test_dir_ + L"\\dir1");
+ file_util::CreateDirectory(test_dir_ + L"\\dir2");
+ CreateTextFile(test_dir_ + L"\\dir2\\dir2file.txt", L"");
+ file_util::CreateDirectory(test_dir_ + L"\\dir2\\inner");
+ CreateTextFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt", L"");
+ CreateTextFile(test_dir_ + L"\\file1.txt", L"");
+ CreateTextFile(test_dir_ + L"\\file2.txt", L"");
+
+ // Only enumerate files.
+ file_util::FileEnumerator f1(test_dir_, true,
+ file_util::FileEnumerator::FILES);
+ FindResultCollector c1(f1);
+ EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file1.txt"));
+ EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\file2.txt"));
+ EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+ EXPECT_TRUE(c1.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+ // Only enumerate directories.
+ file_util::FileEnumerator f2(test_dir_, true,
+ file_util::FileEnumerator::DIRECTORIES);
+ FindResultCollector c2(f2);
+ EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir1"));
+ EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2"));
+ EXPECT_TRUE(c2.HasFile(test_dir_ + L"\\dir2\\inner"));
+
+ // Enumerate files and directories.
+ file_util::FileEnumerator f3(test_dir_, true,
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+ FindResultCollector c3(f3);
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir1"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file1.txt"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\file2.txt"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner"));
+ EXPECT_TRUE(c3.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+ // Non-recursive operation.
+ file_util::FileEnumerator f4(test_dir_, false,
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+ FindResultCollector c4(f4);
+ EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir1"));
+ EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\dir2"));
+ EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file1.txt"));
+ EXPECT_TRUE(c4.HasFile(test_dir_ + L"\\file2.txt"));
+
+ // Enumerate with a pattern.
+ file_util::FileEnumerator f5(test_dir_, true,
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES, L"dir*");
+ FindResultCollector c5(f5);
+ EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir1"));
+ EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2"));
+ EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\dir2file.txt"));
+ EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner"));
+ EXPECT_TRUE(c5.HasFile(test_dir_ + L"\\dir2\\inner\\innerfile.txt"));
+
+ // Make sure the destructor closes the find handle while in the middle of a
+ // query to allow TearDown to delete the directory.
+ file_util::FileEnumerator f6(test_dir_, true,
+ file_util::FileEnumerator::FILES_AND_DIRECTORIES);
+ EXPECT_FALSE(f6.Next().empty()); // Should have found something
+ // (we don't care what).
+}
diff --git a/base/file_version_info.cc b/base/file_version_info.cc
new file mode 100644
index 0000000..90aedd2
--- /dev/null
+++ b/base/file_version_info.cc
@@ -0,0 +1,205 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/file_version_info.h"
+
+#include "base/logging.h"
+#include "base/path_service.h"
+
+// This has to be last.
+#include <strsafe.h>
+
+FileVersionInfo::FileVersionInfo(void* data, int language, int code_page)
+ : language_(language), code_page_(code_page) {
+ data_.reset((char*) data);
+ fixed_file_info_ = NULL;
+ UINT size;
+ ::VerQueryValue(data_.get(), L"\\", (LPVOID*)&fixed_file_info_, &size);
+}
+
+FileVersionInfo::~FileVersionInfo() {
+ DCHECK(data_.get());
+}
+
+typedef struct {
+ WORD language;
+ WORD code_page;
+} LanguageAndCodePage;
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() {
+ std::wstring app_path;
+ if (!PathService::Get(base::FILE_MODULE, &app_path))
+ return NULL;
+
+ return CreateFileVersionInfo(app_path);
+}
+
+// static
+FileVersionInfo* FileVersionInfo::CreateFileVersionInfo(
+ const std::wstring& file_path) {
+ DWORD dummy;
+ const wchar_t* path = file_path.c_str();
+ DWORD length = ::GetFileVersionInfoSize(path, &dummy);
+ if (length == 0)
+ return NULL;
+
+ void* data = calloc(length, 1);
+ if (!data)
+ return NULL;
+
+ if (!::GetFileVersionInfo(path, dummy, length, data)) {
+ free(data);
+ return NULL;
+ }
+
+ LanguageAndCodePage* translate = NULL;
+ uint32 page_count;
+ BOOL query_result = VerQueryValue(data, L"\\VarFileInfo\\Translation",
+ (void**) &translate, &page_count);
+
+ if (query_result && translate) {
+ return new FileVersionInfo(data, translate->language,
+ translate->code_page);
+
+ } else {
+ free(data);
+ return NULL;
+ }
+}
+
+std::wstring FileVersionInfo::company_name() {
+ return GetStringValue(L"CompanyName");
+}
+
+std::wstring FileVersionInfo::company_short_name() {
+ return GetStringValue(L"CompanyShortName");
+}
+
+std::wstring FileVersionInfo::internal_name() {
+ return GetStringValue(L"InternalName");
+}
+
+std::wstring FileVersionInfo::product_name() {
+ return GetStringValue(L"ProductName");
+}
+
+std::wstring FileVersionInfo::product_short_name() {
+ return GetStringValue(L"ProductShortName");
+}
+
+std::wstring FileVersionInfo::comments() {
+ return GetStringValue(L"Comments");
+}
+
+std::wstring FileVersionInfo::legal_copyright() {
+ return GetStringValue(L"LegalCopyright");
+}
+
+std::wstring FileVersionInfo::product_version() {
+ return GetStringValue(L"ProductVersion");
+}
+
+std::wstring FileVersionInfo::file_description() {
+ return GetStringValue(L"FileDescription");
+}
+
+std::wstring FileVersionInfo::legal_trademarks() {
+ return GetStringValue(L"LegalTrademarks");
+}
+
+std::wstring FileVersionInfo::private_build() {
+ return GetStringValue(L"PrivateBuild");
+}
+
+std::wstring FileVersionInfo::file_version() {
+ return GetStringValue(L"FileVersion");
+}
+
+std::wstring FileVersionInfo::original_filename() {
+ return GetStringValue(L"OriginalFilename");
+}
+
+std::wstring FileVersionInfo::special_build() {
+ return GetStringValue(L"SpecialBuild");
+}
+
+std::wstring FileVersionInfo::last_change() {
+ return GetStringValue(L"LastChange");
+}
+
+bool FileVersionInfo::is_official_build() {
+ return (GetStringValue(L"Official Build").compare(L"1") == 0);
+}
+
+bool FileVersionInfo::GetValue(const wchar_t* name, std::wstring* value_str) {
+
+ WORD lang_codepage[8];
+ int i = 0;
+ // Use the language and codepage from the DLL.
+ lang_codepage[i++] = language_;
+ lang_codepage[i++] = code_page_;
+ // Use the default language and codepage from the DLL.
+ lang_codepage[i++] = ::GetUserDefaultLangID();
+ lang_codepage[i++] = code_page_;
+ // Use the language from the DLL and Latin codepage (most common).
+ lang_codepage[i++] = language_;
+ lang_codepage[i++] = 1252;
+ // Use the default language and Latin codepage (most common).
+ lang_codepage[i++] = ::GetUserDefaultLangID();
+ lang_codepage[i++] = 1252;
+
+ i = 0;
+ while (i < arraysize(lang_codepage)) {
+ wchar_t sub_block[MAX_PATH];
+ WORD language = lang_codepage[i++];
+ WORD code_page = lang_codepage[i++];
+ _snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
+ L"\\StringFileInfo\\%04x%04x\\%s", language, code_page, name);
+ LPVOID value = NULL;
+ uint32 size;
+ BOOL r = ::VerQueryValue(data_.get(), sub_block, &value, &size);
+ if (r && value) {
+ value_str->assign(static_cast<wchar_t*>(value));
+ return true;
+ }
+ }
+ return false;
+}
+
+std::wstring FileVersionInfo::GetStringValue(const wchar_t* name) {
+ std::wstring str;
+ if (GetValue(name, &str))
+ return str;
+ else
+ return L"";
+}
+
diff --git a/base/file_version_info.h b/base/file_version_info.h
new file mode 100644
index 0000000..8d82e87
--- /dev/null
+++ b/base/file_version_info.h
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_FILE_VERSION_INFO_H__
+#define BASE_FILE_VERSION_INFO_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+// Provides a way to access the version information for a file.
+// This is the information you access when you select a file in the Windows
+// explorer, right-click select Properties, then click the Version tab.
+
+class FileVersionInfo {
+ public:
+ // Creates a FileVersionInfo for the specified path. Returns NULL if something
+ // goes wrong (typically the file does not exit or cannot be opened). The
+ // returned object should be deleted when you are done with it.
+ static FileVersionInfo* CreateFileVersionInfo(const std::wstring& file_path);
+
+ // Creates a FileVersionInfo for the current application. Returns NULL in case
+ // of error. The returned object should be deleted when you are done with it.
+ static FileVersionInfo*
+ FileVersionInfo::CreateFileVersionInfoForCurrentModule();
+
+ ~FileVersionInfo();
+
+ // Accessors to the different version properties.
+ // Returns an empty string if the property is not found.
+ std::wstring company_name();
+ std::wstring company_short_name();
+ std::wstring product_name();
+ std::wstring product_short_name();
+ std::wstring internal_name();
+ std::wstring product_version();
+ std::wstring private_build();
+ std::wstring special_build();
+ std::wstring comments();
+ std::wstring original_filename();
+ std::wstring file_description();
+ std::wstring file_version();
+ std::wstring legal_copyright();
+ std::wstring legal_trademarks();
+ std::wstring last_change();
+ bool is_official_build();
+
+ // Lets you access other properties not covered above.
+ bool GetValue(const wchar_t* name, std::wstring* value);
+
+ // Similar to GetValue but returns a wstring (empty string if the property
+ // does not exist).
+ std::wstring GetStringValue(const wchar_t* name);
+
+ // Get the fixed file info if it exists. Otherwise NULL
+ VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; }
+
+ private:
+ FileVersionInfo(void* data, int language, int code_page);
+
+ scoped_ptr_malloc<char> data_;
+ int language_;
+ int code_page_;
+ // This is a pointer into the data_ if it exists. Otherwise NULL.
+ VS_FIXEDFILEINFO* fixed_file_info_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(FileVersionInfo);
+};
+
+#endif // BASE_FILE_VERSION_INFO_H__
diff --git a/base/file_version_info_unittest.cc b/base/file_version_info_unittest.cc
new file mode 100644
index 0000000..272cba8f
--- /dev/null
+++ b/base/file_version_info_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/scoped_ptr.h"
+#include "base/file_version_info.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class FileVersionInfoTest : public testing::Test {
+};
+
+std::wstring GetTestDataPath() {
+ std::wstring path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &path);
+ file_util::AppendToPath(&path, L"base");
+ file_util::AppendToPath(&path, L"data");
+ file_util::AppendToPath(&path, L"file_version_info_unittest");
+ return path;
+}
+
+}
+
+TEST(FileVersionInfoTest, HardCodedProperties) {
+ const wchar_t* kDLLNames[] = {
+ L"FileVersionInfoTest1.dll"
+ };
+
+ const wchar_t* kExpectedValues[1][15] = {
+ // FileVersionInfoTest.dll
+ L"Goooooogle", // company_name
+ L"Google", // company_short_name
+ L"This is the product name", // product_name
+ L"This is the product short name", // product_short_name
+ L"The Internal Name", // internal_name
+ L"4.3.2.1", // product_version
+ L"Private build property", // private_build
+ L"Special build property", // special_build
+ L"This is a particularly interesting comment", // comments
+ L"This is the original filename", // original_filename
+ L"This is my file description", // file_description
+ L"1.2.3.4", // file_version
+ L"This is the legal copyright", // legal_copyright
+ L"This is the legal trademarks", // legal_trademarks
+ L"This is the last change", // last_change
+
+ };
+
+ for (int i = 0; i < arraysize(kDLLNames); ++i) {
+ std::wstring dll_path = GetTestDataPath();
+ file_util::AppendToPath(&dll_path, kDLLNames[i]);
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ int j = 0;
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->company_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->company_short_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_short_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->internal_name());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->product_version());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->private_build());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->special_build());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->comments());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->original_filename());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->file_description());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->file_version());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_copyright());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_trademarks());
+ EXPECT_EQ(kExpectedValues[i][j++], version_info->last_change());
+ }
+}
+
+TEST(FileVersionInfoTest, IsOfficialBuild) {
+ const wchar_t* kDLLNames[] = {
+ L"FileVersionInfoTest1.dll",
+ L"FileVersionInfoTest2.dll"
+ };
+
+ const bool kExpected[] = {
+ true,
+ false,
+ };
+
+ // Test consistency check.
+ ASSERT_EQ(arraysize(kDLLNames), arraysize(kExpected));
+
+ for (int i = 0; i < arraysize(kDLLNames); ++i) {
+ std::wstring dll_path = GetTestDataPath();
+ file_util::AppendToPath(&dll_path, kDLLNames[i]);
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ EXPECT_EQ(kExpected[i], version_info->is_official_build());
+ }
+}
+
+TEST(FileVersionInfoTest, CustomProperties) {
+ std::wstring dll_path = GetTestDataPath();
+ file_util::AppendToPath(&dll_path, L"FileVersionInfoTest1.dll");
+
+ scoped_ptr<FileVersionInfo> version_info(
+ FileVersionInfo::CreateFileVersionInfo(dll_path));
+
+ // Test few existing properties.
+ std::wstring str;
+ EXPECT_TRUE(version_info->GetValue(L"Custom prop 1", &str));
+ EXPECT_EQ(L"Un", str);
+ EXPECT_EQ(L"Un", version_info->GetStringValue(L"Custom prop 1"));
+
+ EXPECT_TRUE(version_info->GetValue(L"Custom prop 2", &str));
+ EXPECT_EQ(L"Deux", str);
+ EXPECT_EQ(L"Deux", version_info->GetStringValue(L"Custom prop 2"));
+
+ EXPECT_TRUE(version_info->GetValue(L"Custom prop 3", &str));
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str);
+ EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043",
+ version_info->GetStringValue(L"Custom prop 3"));
+
+ // Test an non-existing property.
+ EXPECT_FALSE(version_info->GetValue(L"Unknown property", &str));
+ EXPECT_EQ(L"", version_info->GetStringValue(L"Unknown property"));
+}
diff --git a/base/fix_wp64.h b/base/fix_wp64.h
new file mode 100644
index 0000000..ff82a30
--- /dev/null
+++ b/base/fix_wp64.h
@@ -0,0 +1,100 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Various inline functions and macros to fix compilation of 32 bit target
+// on MSVC with /Wp64 flag enabled.
+
+#ifndef BASE_FIX_WP64_H__
+#define BASE_FIX_WP64_H__
+
+#include <windows.h>
+
+// Platform SDK fixes when building with /Wp64 for a 32 bits target.
+#if !defined(_WIN64) && defined(_Wp64)
+
+#ifdef InterlockedExchangePointer
+#undef InterlockedExchangePointer
+// The problem is that the macro provided for InterlockedExchangePointer() is
+// doing a (LONG) C-style cast that triggers invariably the warning C4312 when
+// building on 32 bits.
+inline void* InterlockedExchangePointer(void* volatile* target, void* value) {
+ return reinterpret_cast<void*>(static_cast<LONG_PTR>(InterlockedExchange(
+ reinterpret_cast<volatile LONG*>(target),
+ static_cast<LONG>(reinterpret_cast<LONG_PTR>(value)))));
+}
+#endif // #ifdef InterlockedExchangePointer
+
+#ifdef SetWindowLongPtrA
+#undef SetWindowLongPtrA
+// When build on 32 bits, SetWindowLongPtrX() is a macro that redirects to
+// SetWindowLongX(). The problem is that this function takes a LONG argument
+// instead of a LONG_PTR.
+inline LONG_PTR SetWindowLongPtrA(HWND window, int index, LONG_PTR new_long) {
+ return ::SetWindowLongA(window, index, static_cast<LONG>(new_long));
+}
+#endif // #ifdef SetWindowLongPtrA
+
+#ifdef SetWindowLongPtrW
+#undef SetWindowLongPtrW
+inline LONG_PTR SetWindowLongPtrW(HWND window, int index, LONG_PTR new_long) {
+ return ::SetWindowLongW(window, index, static_cast<LONG>(new_long));
+}
+#endif // #ifdef SetWindowLongPtrW
+
+#ifdef GetWindowLongPtrA
+#undef GetWindowLongPtrA
+inline LONG_PTR GetWindowLongPtrA(HWND window, int index) {
+ return ::GetWindowLongA(window, index);
+}
+#endif // #ifdef GetWindowLongPtrA
+
+#ifdef GetWindowLongPtrW
+#undef GetWindowLongPtrW
+inline LONG_PTR GetWindowLongPtrW(HWND window, int index) {
+ return ::GetWindowLongW(window, index);
+}
+#endif // #ifdef GetWindowLongPtrW
+
+#ifdef SetClassLongPtrA
+#undef SetClassLongPtrA
+inline LONG_PTR SetClassLongPtrA(HWND window, int index, LONG_PTR new_long) {
+ return ::SetClassLongA(window, index, static_cast<LONG>(new_long));
+}
+#endif // #ifdef SetClassLongPtrA
+
+#ifdef SetClassLongPtrW
+#undef SetClassLongPtrW
+inline LONG_PTR SetClassLongPtrW(HWND window, int index, LONG_PTR new_long) {
+ return ::SetClassLongW(window, index, static_cast<LONG>(new_long));
+}
+#endif // #ifdef SetClassLongPtrW
+
+#endif // #if !defined(_WIN64) && defined(_Wp64)
+
+#endif // BASE_FIX_WP64_H__
diff --git a/base/fixed_string.h b/base/fixed_string.h
new file mode 100644
index 0000000..857b057
--- /dev/null
+++ b/base/fixed_string.h
@@ -0,0 +1,96 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_FIXED_STRING_H__
+#define BASE_FIXED_STRING_H__
+
+#include <string.h>
+
+#include "base/string_util.h"
+
+// This class manages a fixed-size, null-terminated string buffer. It is meant
+// to be allocated on the stack, and it makes no use of the heap internally. In
+// most cases you'll just want to use a std::(w)string, but when you need to
+// avoid the heap, you can use this class instead.
+//
+// Methods are provided to read the null-terminated buffer and to append data
+// to the buffer, and once the buffer fills-up, it simply discards any extra
+// append calls.
+//
+// Since this object clips if the internal fixed buffer is exceeded, it is
+// appropriate for exception handlers where the heap may be corrupted. Fixed
+// buffers that overflow onto the heap are provided by Stack[W]String.
+// (see stack_container.h).
+template <class CharT, int MaxSize>
+class FixedString {
+ public:
+ typedef CharTraits<CharT> char_traits;
+
+ FixedString() : index_(0), truncated_(false) {
+ buf_[0] = CharT(0);
+ }
+
+ // Returns true if the Append ever failed.
+ bool was_truncated() const { return truncated_; }
+
+ // Returns the number of characters in the string, excluding the null
+ // terminator.
+ size_t size() const { return index_; }
+
+ // Returns the null-terminated string.
+ const CharT* get() const { return buf_; }
+ CharT* get() { return buf_; }
+
+ // Append an array of characters. The operation is bounds checked, and if
+ // there is insufficient room, then the was_truncated() flag is set to true.
+ void Append(const CharT* s, size_t n) {
+ if (char_traits::copy_num(buf_ + index_, arraysize(buf_) - index_, s, n)) {
+ index_ += n;
+ } else {
+ truncated_ = true;
+ }
+ }
+
+ // Append a null-terminated string.
+ void Append(const CharT* s) {
+ Append(s, char_traits::length(s));
+ }
+
+ // Append a single character.
+ void Append(CharT c) {
+ Append(&c, 1);
+ }
+
+ private:
+ CharT buf_[MaxSize];
+ size_t index_;
+ bool truncated_;
+};
+
+#endif // BASE_FIXED_STRING_H__
diff --git a/base/fixed_string_unittest.cc b/base/fixed_string_unittest.cc
new file mode 100644
index 0000000..a7a0767
--- /dev/null
+++ b/base/fixed_string_unittest.cc
@@ -0,0 +1,62 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/fixed_string.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class FixedStringTest : public testing::Test {
+ };
+}
+
+TEST(FixedStringTest, TestBasic) {
+ const wchar_t kData[] = L"hello world";
+
+ FixedString<wchar_t, 40> buf;
+
+ buf.Append(kData);
+ EXPECT_EQ(arraysize(kData)-1, buf.size());
+ EXPECT_EQ(0, wcscmp(kData, buf.get()));
+
+ buf.Append(' ');
+ buf.Append(kData);
+ const wchar_t kExpected[] = L"hello world hello world";
+ EXPECT_EQ(arraysize(kExpected)-1, buf.size());
+ EXPECT_EQ(0, wcscmp(kExpected, buf.get()));
+ EXPECT_EQ(false, buf.was_truncated());
+}
+
+// Disable this test in debug builds since overflow asserts.
+TEST(FixedStringTest, TestOverflow) {
+ FixedString<wchar_t, 5> buf;
+ buf.Append(L"hello world");
+ EXPECT_EQ(0, buf.size());
+ EXPECT_EQ(0, buf.get()[0]);
+ EXPECT_EQ(true, buf.was_truncated());
+}
diff --git a/base/gfx/SConscript b/base/gfx/SConscript
new file mode 100644
index 0000000..4ec9e50
--- /dev/null
+++ b/base/gfx/SConscript
@@ -0,0 +1,83 @@
+# Copyright 2008, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Import('env')
+
+env = env.Clone()
+
+env.Prepend(
+ CPPPATH = [
+ '$SKIA_DIR/include',
+ '$SKIA_DIR/include/corecg',
+ '$SKIA_DIR/include/platform',
+ '$ZLIB_DIR',
+ '$LIBPNG_DIR',
+ '$ICU38_DIR/public/common',
+ '$ICU38_DIR/public/i18n',
+ '../..',
+ ],
+ CPPDEFINES = [
+ 'PNG_USER_CONFIG',
+ 'CHROME_PNG_WRITE_SUPPORT',
+ 'U_STATIC_IMPLEMENTATION',
+ 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS',
+ 'WIN32_LEAN_AND_MEAN',
+ ],
+ CCFLAGS = [
+ '/TP',
+
+ '/Wp64',
+ '/WX',
+
+ '/wd4503',
+ '/wd4819',
+ ],
+)
+
+input_files = [
+ 'bitmap_header.cc',
+ 'bitmap_platform_device.cc',
+ 'convolver.cc',
+ 'font_utils.cc',
+ 'image_operations.cc',
+ 'native_theme.cc',
+ 'platform_canvas.cc',
+ 'platform_device.cc',
+ 'png_decoder.cc',
+ 'png_encoder.cc',
+ 'point.cc',
+ 'rect.cc',
+ 'size.cc',
+ 'skia_utils.cc',
+ 'uniscribe.cc',
+ 'vector_canvas.cc',
+ 'vector_device.cc',
+]
+
+env.StaticLibrary('base_gfx', input_files)
diff --git a/base/gfx/bitmap_header.cc b/base/gfx/bitmap_header.cc
new file mode 100644
index 0000000..3c54694
--- /dev/null
+++ b/base/gfx/bitmap_header.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/bitmap_header.h"
+
+namespace gfx {
+
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr) {
+ CreateBitmapHeaderWithColorDepth(width, height, 32, hdr);
+}
+
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+ BITMAPINFOHEADER* hdr) {
+ // These values are shared with gfx::PlatformDevice
+ hdr->biSize = sizeof(BITMAPINFOHEADER);
+ hdr->biWidth = width;
+ hdr->biHeight = -height; // minus means top-down bitmap
+ hdr->biPlanes = 1;
+ hdr->biBitCount = color_depth;
+ hdr->biCompression = BI_RGB; // no compression
+ hdr->biSizeImage = 0;
+ hdr->biXPelsPerMeter = 1;
+ hdr->biYPelsPerMeter = 1;
+ hdr->biClrUsed = 0;
+ hdr->biClrImportant = 0;
+}
+
+
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr) {
+ // Because bmp v4 header is just an extension, we just create a v3 header and
+ // copy the bits over to the v4 header.
+ BITMAPINFOHEADER header_v3;
+ CreateBitmapHeader(width, height, &header_v3);
+ memset(hdr, 0, sizeof(BITMAPV4HEADER));
+ memcpy(hdr, &header_v3, sizeof(BITMAPINFOHEADER));
+
+ // Correct the size of the header and fill in the mask values.
+ hdr->bV4Size = sizeof(BITMAPV4HEADER);
+ hdr->bV4RedMask = 0x00ff0000;
+ hdr->bV4GreenMask = 0x0000ff00;
+ hdr->bV4BlueMask = 0x000000ff;
+ hdr->bV4AlphaMask = 0xff000000;
+}
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width,
+ int height,
+ BITMAPINFOHEADER* hdr) {
+ hdr->biSize = sizeof(BITMAPINFOHEADER);
+ hdr->biWidth = width;
+ hdr->biHeight = -height;
+ hdr->biPlanes = 1;
+ hdr->biBitCount = 1;
+ hdr->biCompression = BI_RGB;
+ hdr->biSizeImage = 0;
+ hdr->biXPelsPerMeter = 1;
+ hdr->biYPelsPerMeter = 1;
+ hdr->biClrUsed = 0;
+ hdr->biClrImportant = 0;
+}
+
+} // namespace gfx
diff --git a/base/gfx/bitmap_header.h b/base/gfx/bitmap_header.h
new file mode 100644
index 0000000..e8179c8
--- /dev/null
+++ b/base/gfx/bitmap_header.h
@@ -0,0 +1,56 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_BITMAP_HEADER_H__
+#define BASE_GFX_BITMAP_HEADER_H__
+
+#include <windows.h>
+
+namespace gfx {
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size.
+void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPINFOHEADER structure given the bitmap's size and
+// color depth in bits per pixel.
+void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth,
+ BITMAPINFOHEADER* hdr);
+
+// Creates a BITMAPV4HEADER structure given the bitmap's size. You probably
+// only need to use BMP V4 if you need transparency (alpha channel). This
+// function sets the AlphaMask to 0xff000000.
+void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr);
+
+// Creates a monochrome bitmap header.
+void CreateMonochromeBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr);
+
+
+} // namespace gfx
+
+#endif // BASE_GFX_BITMAP_HEADER_H__
diff --git a/base/gfx/bitmap_platform_device.cc b/base/gfx/bitmap_platform_device.cc
new file mode 100644
index 0000000..0a914e2
--- /dev/null
+++ b/base/gfx/bitmap_platform_device.cc
@@ -0,0 +1,482 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/bitmap_platform_device.h"
+
+#include "base/gfx/bitmap_header.h"
+#include "base/logging.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+#include "SkUtils.h"
+
+namespace gfx {
+
+// When Windows draws text, is sets the fourth byte (which Skia uses for alpha)
+// to zero. This means that if we try compositing with text that Windows has
+// drawn, we get invalid color values (if the alpha is 0, the other channels
+// should be 0 since Skia uses premultiplied colors) and strange results.
+//
+// HTML rendering only requires one bit of transparency. When you ask for a
+// semitransparent div, the div itself is drawn in another layer as completely
+// opaque, and then composited onto the lower layer with a transfer function.
+// The only place an alpha channel is needed is to track what has been drawn
+// and what has not been drawn.
+//
+// Therefore, when we allocate a new device, we fill it with this special
+// color. Because Skia uses premultiplied colors, any color where the alpha
+// channel is smaller than any component is impossible, so we know that no
+// legitimate drawing will produce this color. We use 1 as the alpha value
+// because 0 is produced when Windows draws text (even though it should be
+// opaque).
+//
+// When a layer is done and we want to render it to a lower layer, we use
+// fixupAlphaBeforeCompositing. This replaces all 0 alpha channels with
+// opaque (to fix the text problem), and replaces this magic color value
+// with transparency. The result is something that can be correctly
+// composited. However, once this has been done, no more can be drawn to
+// the layer because fixing the alphas *again* will result in incorrect
+// values.
+static const uint32_t kMagicTransparencyColor = 0x01FFFEFD;
+
+namespace {
+
+// Constrains position and size to fit within available_size. If |size| is -1,
+// all the available_size is used. Returns false if the position is out of
+// available_size.
+bool Constrain(int available_size, int* position, int *size) {
+ if (*size < -2)
+ return false;
+
+ if (*position < 0) {
+ if (*size != -1)
+ *size += *position;
+ *position = 0;
+ }
+ if (*size == 0 || *position >= available_size)
+ return false;
+
+ if (*size > 0) {
+ int overflow = (*position + *size) - available_size;
+ if (overflow > 0) {
+ *size -= overflow;
+ }
+ } else {
+ // Fill up available size.
+ *size = available_size - *position;
+ }
+ return true;
+}
+
+// If the pixel value is 0, it gets set to kMagicTransparencyColor.
+void PrepareAlphaForGDI(uint32_t* pixel) {
+ if (*pixel == 0) {
+ *pixel = kMagicTransparencyColor;
+ }
+}
+
+// If the pixel value is kMagicTransparencyColor, it gets set to 0. Otherwise
+// if the alpha is 0, the alpha is set to 255.
+void PostProcessAlphaForGDI(uint32_t* pixel) {
+ if (*pixel == kMagicTransparencyColor) {
+ *pixel = 0;
+ } else if ((*pixel & 0xFF000000) == 0) {
+ *pixel |= 0xFF000000;
+ }
+}
+
+// Sets the opacity of the specified value to 0xFF.
+void MakeOpaqueAlphaAdjuster(uint32_t* pixel) {
+ *pixel |= 0xFF000000;
+}
+
+// See the declaration of kMagicTransparencyColor at the top of the file.
+void FixupAlphaBeforeCompositing(uint32_t* pixel) {
+ if (*pixel == kMagicTransparencyColor)
+ *pixel = 0;
+ else
+ *pixel |= 0xFF000000;
+}
+
+} // namespace
+
+class BitmapPlatformDevice::BitmapPlatformDeviceData
+ : public base::RefCounted<BitmapPlatformDeviceData> {
+ public:
+ explicit BitmapPlatformDeviceData(HBITMAP hbitmap);
+
+ // Create/destroy hdc_, which is the memory DC for our bitmap data.
+ HDC GetBitmapDC();
+ void ReleaseBitmapDC();
+ bool IsBitmapDCCreated() const;
+
+ // Sets the transform and clip operations. This will not update the DC,
+ // but will mark the config as dirty. The next call of LoadConfig will
+ // pick up these changes.
+ void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);
+ // The device offset is already modified according to the transformation.
+ void SetDeviceOffset(int x, int y);
+
+ const SkMatrix& transform() const {
+ return transform_;
+ }
+
+ protected:
+ // Loads the current transform (taking into account offset_*_) and clip
+ // into the DC. Can be called even when the DC is NULL (will be a NOP).
+ void LoadConfig();
+
+ // Windows bitmap corresponding to our surface.
+ HBITMAP hbitmap_;
+
+ // Lazily-created DC used to draw into the bitmap, see getBitmapDC.
+ HDC hdc_;
+
+ // Additional offset applied to the transform. See setDeviceOffset().
+ int offset_x_;
+ int offset_y_;
+
+ // True when there is a transform or clip that has not been set to the DC.
+ // The DC is retrieved for every text operation, and the transform and clip
+ // do not change as much. We can save time by not loading the clip and
+ // transform for every one.
+ bool config_dirty_;
+
+ // Translation assigned to the DC: we need to keep track of this separately
+ // so it can be updated even if the DC isn't created yet.
+ SkMatrix transform_;
+
+ // The current clipping
+ SkRegion clip_region_;
+
+ private:
+ friend class base::RefCounted<BitmapPlatformDeviceData>;
+ ~BitmapPlatformDeviceData();
+
+ DISALLOW_EVIL_CONSTRUCTORS(BitmapPlatformDeviceData);
+};
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
+ HBITMAP hbitmap)
+ : hbitmap_(hbitmap),
+ hdc_(NULL),
+ offset_x_(0),
+ offset_y_(0),
+ config_dirty_(true) { // Want to load the config next time.
+ // Initialize the clip region to the entire bitmap.
+ BITMAP bitmap_data;
+ if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) {
+ SkIRect rect;
+ rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
+ clip_region_ = SkRegion(rect);
+ }
+
+ transform_.reset();
+}
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
+ if (hdc_)
+ ReleaseBitmapDC();
+
+ // this will free the bitmap data as well as the bitmap handle
+ DeleteObject(hbitmap_);
+}
+
+HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() {
+ if (!hdc_) {
+ hdc_ = CreateCompatibleDC(NULL);
+ InitializeDC(hdc_);
+ HGDIOBJ old_bitmap = SelectObject(hdc_, hbitmap_);
+ // When the memory DC is created, its display surface is exactly one
+ // monochrome pixel wide and one monochrome pixel high. Since we select our
+ // own bitmap, we must delete the previous one.
+ DeleteObject(old_bitmap);
+ }
+
+ LoadConfig();
+ return hdc_;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC() {
+ DCHECK(hdc_);
+ DeleteDC(hdc_);
+ hdc_ = NULL;
+}
+
+bool BitmapPlatformDevice::BitmapPlatformDeviceData::IsBitmapDCCreated() const {
+ return hdc_ != NULL;
+}
+
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
+ const SkMatrix& transform,
+ const SkRegion& region) {
+ transform_ = transform;
+ clip_region_ = region;
+ config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetDeviceOffset(int x,
+ int y) {
+ offset_x_ = x;
+ offset_y_ = y;
+ config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
+ if (!config_dirty_ || !hdc_)
+ return; // Nothing to do.
+ config_dirty_ = false;
+
+ // Transform.
+ SkMatrix t(transform_);
+ t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+ LoadTransformToDC(hdc_, t);
+ // We don't use transform_ for the clipping region since the translation is
+ // already applied to offset_x_ and offset_y_.
+ t.reset();
+ t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+ LoadClippingRegionToDC(hdc_, clip_region_, t);
+}
+
+// We use this static factory function instead of the regular constructor so
+// that we can create the pixel data before calling the constructor. This is
+// required so that we can call the base class' constructor with the pixel
+// data.
+BitmapPlatformDevice* BitmapPlatformDevice::create(HDC screen_dc,
+ int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ SkBitmap bitmap;
+
+ // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
+ // we just expand it here.
+ if (width == 0)
+ width = 1;
+ if (height == 0)
+ height = 1;
+
+ BITMAPINFOHEADER hdr;
+ CreateBitmapHeader(width, height, &hdr);
+
+ void* data;
+ HBITMAP hbitmap = CreateDIBSection(screen_dc,
+ reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+ &data,
+ shared_section, 0);
+
+ // If we run out of GDI objects or some other error occurs, we won't get a
+ // bitmap here. This will cause us to crash later because the data pointer is
+ // NULL. To make sure that we can assign blame for those crashes to this code,
+ // we deliberately crash here, even in release mode.
+ CHECK(hbitmap);
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.setPixels(data);
+ bitmap.setIsOpaque(is_opaque);
+
+ if (is_opaque) {
+#ifndef NDEBUG
+ // To aid in finding bugs, we set the background color to something
+ // obviously wrong so it will be noticable when it is not cleared
+ bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
+#endif
+ } else {
+ // A transparent layer is requested: fill with our magic "transparent"
+ // color, see the declaration of kMagicTransparencyColor above
+ sk_memset32(static_cast<uint32_t*>(data), kMagicTransparencyColor,
+ width * height);
+ }
+
+ // The device object will take ownership of the HBITMAP.
+ return new BitmapPlatformDevice(new BitmapPlatformDeviceData(hbitmap), bitmap);
+}
+
+// The device will own the HBITMAP, which corresponds to also owning the pixel
+// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+BitmapPlatformDevice::BitmapPlatformDevice(BitmapPlatformDeviceData* data,
+ const SkBitmap& bitmap)
+ : PlatformDevice(bitmap),
+ data_(data) {
+}
+
+// The copy constructor just adds another reference to the underlying data.
+// We use a const cast since the default Skia definitions don't define the
+// proper constedness that we expect (accessBitmap should really be const).
+BitmapPlatformDevice::BitmapPlatformDevice(const BitmapPlatformDevice& other)
+ : PlatformDevice(
+ const_cast<BitmapPlatformDevice&>(other).accessBitmap(true)),
+ data_(other.data_) {
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+}
+
+BitmapPlatformDevice& BitmapPlatformDevice::operator=(
+ const BitmapPlatformDevice& other) {
+ data_ = other.data_;
+ return *this;
+}
+
+HDC BitmapPlatformDevice::getBitmapDC() {
+ return data_->GetBitmapDC();
+}
+
+void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region) {
+ data_->SetMatrixClip(transform, region);
+}
+
+void BitmapPlatformDevice::setDeviceOffset(int x, int y) {
+ data_->SetDeviceOffset(x, y);
+}
+
+void BitmapPlatformDevice::drawToHDC(HDC dc, int x, int y,
+ const RECT* src_rect) {
+ bool created_dc = !data_->IsBitmapDCCreated();
+ HDC source_dc = getBitmapDC();
+
+ RECT temp_rect;
+ if (!src_rect) {
+ temp_rect.left = 0;
+ temp_rect.right = width();
+ temp_rect.top = 0;
+ temp_rect.bottom = height();
+ src_rect = &temp_rect;
+ }
+
+ int copy_width = src_rect->right - src_rect->left;
+ int copy_height = src_rect->bottom - src_rect->top;
+
+ // We need to reset the translation for our bitmap or (0,0) won't be in the
+ // upper left anymore
+ SkMatrix identity;
+ identity.reset();
+
+ LoadTransformToDC(source_dc, identity);
+ if (isOpaque()) {
+ BitBlt(dc,
+ x,
+ y,
+ copy_width,
+ copy_height,
+ source_dc,
+ src_rect->left,
+ src_rect->top,
+ SRCCOPY);
+ } else {
+ BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+ AlphaBlend(dc,
+ x,
+ y,
+ copy_width,
+ copy_height,
+ source_dc,
+ src_rect->left,
+ src_rect->top,
+ copy_width,
+ copy_height,
+ blend_function);
+ }
+ LoadTransformToDC(source_dc, data_->transform());
+
+ if (created_dc)
+ data_->ReleaseBitmapDC();
+}
+
+void BitmapPlatformDevice::prepareForGDI(int x, int y, int width, int height) {
+ processPixels<PrepareAlphaForGDI>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::postProcessGDI(int x, int y, int width, int height) {
+ processPixels<PostProcessAlphaForGDI>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::makeOpaque(int x, int y, int width, int height) {
+ processPixels<MakeOpaqueAlphaAdjuster>(x, y, width, height);
+}
+
+void BitmapPlatformDevice::fixupAlphaBeforeCompositing() {
+ const SkBitmap& bitmap = accessBitmap(true);
+ SkAutoLockPixels lock(bitmap);
+ uint32_t* data = bitmap.getAddr32(0, 0);
+
+ size_t words = bitmap.rowBytes() / sizeof(uint32_t) * bitmap.height();
+ for (size_t i = 0; i < words; i++) {
+ if (data[i] == kMagicTransparencyColor)
+ data[i] = 0;
+ else
+ data[i] |= 0xFF000000;
+ }
+}
+
+// Returns the color value at the specified location.
+SkColor BitmapPlatformDevice::getColorAt(int x, int y) {
+ const SkBitmap& bitmap = accessBitmap(false);
+ SkAutoLockPixels lock(bitmap);
+ uint32_t* data = bitmap.getAddr32(0, 0);
+ return static_cast<SkColor>(data[x + y * width()]);
+}
+
+void BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
+ // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
+ // operation has occurred on our DC.
+ if (data_->IsBitmapDCCreated())
+ GdiFlush();
+}
+
+template<BitmapPlatformDevice::adjustAlpha adjustor>
+void BitmapPlatformDevice::processPixels(int x,
+ int y,
+ int width,
+ int height) {
+ const SkBitmap& bitmap = accessBitmap(true);
+ DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config);
+ const SkMatrix& matrix = data_->transform();
+ int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x;
+ int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y;
+
+ if (Constrain(bitmap.width(), &bitmap_start_x, &width) &&
+ Constrain(bitmap.height(), &bitmap_start_y, &height)) {
+ SkAutoLockPixels lock(bitmap);
+ DCHECK_EQ(bitmap.rowBytes() % sizeof(uint32_t), 0u);
+ size_t row_words = bitmap.rowBytes() / sizeof(uint32_t);
+ // Set data to the first pixel to be modified.
+ uint32_t* data = bitmap.getAddr32(0, 0) + (bitmap_start_y * row_words) +
+ bitmap_start_x;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ adjustor(data + j);
+ }
+ data += row_words;
+ }
+ }
+}
+
+} // namespace gfx
diff --git a/base/gfx/bitmap_platform_device.h b/base/gfx/bitmap_platform_device.h
new file mode 100644
index 0000000..481067a
--- /dev/null
+++ b/base/gfx/bitmap_platform_device.h
@@ -0,0 +1,135 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
+#define BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
+
+#include "base/gfx/platform_device.h"
+#include "base/ref_counted.h"
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface Windows can also write
+// to. BitmapPlatformDevice creates a bitmap using CreateDIBSection() in a
+// format that Skia supports and can then use this to draw ClearType into, etc.
+// This pixel data is provided to the bitmap that the device contains so that it
+// can be shared.
+//
+// The device owns the pixel data, when the device goes away, the pixel data
+// also becomes invalid. THIS IS DIFFERENT THAN NORMAL SKIA which uses
+// reference counting for the pixel data. In normal Skia, you could assign
+// another bitmap to this device's bitmap and everything will work properly.
+// For us, that other bitmap will become invalid as soon as the device becomes
+// invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
+// DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
+class BitmapPlatformDevice : public PlatformDevice {
+ public:
+ // Factory function. The screen DC is used to create the bitmap, and will not
+ // be stored beyond this function. is_opaque should be set if the caller
+ // knows the bitmap will be completely opaque and allows some optimizations.
+ //
+ // The shared_section parameter is optional (pass NULL for default behavior).
+ // If shared_section is non-null, then it must be a handle to a file-mapping
+ // object returned by CreateFileMapping. See CreateDIBSection for details.
+ static BitmapPlatformDevice* create(HDC screen_dc,
+ int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section);
+
+ // Copy constructor. When copied, devices duplicate their internal data, so
+ // stay linked. This is because their implementation is very heavyweight
+ // (lots of memory and some GDI objects). If a device has been copied, both
+ // clip rects and other state will stay in sync.
+ //
+ // This means it will NOT work to duplicate a device and assign it to a
+ // canvas, because the two canvases will each set their own clip rects, and
+ // the resulting GDI clip rect will be random.
+ //
+ // Copy constucting and "=" is designed for saving the device or passing it
+ // around to another routine willing to deal with the bitmap data directly.
+ BitmapPlatformDevice(const BitmapPlatformDevice& other);
+ virtual ~BitmapPlatformDevice();
+
+ // See warning for copy constructor above.
+ BitmapPlatformDevice& operator=(const BitmapPlatformDevice& other);
+
+ // Retrieves the bitmap DC, which is the memory DC for our bitmap data. The
+ // bitmap DC is lazy created.
+ virtual HDC getBitmapDC();
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region);
+ virtual void setDeviceOffset(int x, int y);
+
+ virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect);
+ virtual void prepareForGDI(int x, int y, int width, int height);
+ virtual void postProcessGDI(int x, int y, int width, int height);
+ virtual void makeOpaque(int x, int y, int width, int height);
+ virtual void fixupAlphaBeforeCompositing();
+ virtual bool IsVectorial() { return false; }
+
+ // Returns the color value at the specified location. This does not
+ // consider any transforms that may be set on the device.
+ SkColor getColorAt(int x, int y);
+
+ protected:
+ // Flushes the Windows device context so that the pixel data can be accessed
+ // directly by Skia. Overridden from SkDevice, this is called when Skia
+ // starts accessing pixel data.
+ virtual void onAccessBitmap(SkBitmap* bitmap);
+
+ private:
+ // Function pointer used by the processPixels method for setting the alpha
+ // value of a particular pixel.
+ typedef void (*adjustAlpha)(uint32_t* pixel);
+
+ // Reference counted data that can be shared between multiple devices. This
+ // allows copy constructors and operator= for devices to work properly. The
+ // bitmaps used by the base device class are already refcounted and copyable.
+ class BitmapPlatformDeviceData;
+
+ // Private constructor.
+ BitmapPlatformDevice(BitmapPlatformDeviceData* data, const SkBitmap& bitmap);
+
+ // Loops through each of the pixels in the specified range, invoking
+ // adjustor for the alpha value of each pixel. If |width| or |height| are -1,
+ // the available width/height is used.
+ template<adjustAlpha adjustor>
+ void processPixels(int x,
+ int y,
+ int width,
+ int height);
+
+ // Data associated with this device, guaranteed non-null.
+ scoped_refptr<BitmapPlatformDeviceData> data_;
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_BITMAP_PLATFORM_DEVICE_H__
diff --git a/base/gfx/convolver.cc b/base/gfx/convolver.cc
new file mode 100644
index 0000000..e56493e
--- /dev/null
+++ b/base/gfx/convolver.cc
@@ -0,0 +1,359 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/gfx/convolver.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline uint8 ClampTo8(int32 a) {
+ if (static_cast<uint32>(a) < 256)
+ return a; // Avoid the extra check in the common case.
+ if (a < 0)
+ return 0;
+ return 255;
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+ // The number of pixels in each row is given in |source_row_pixel_width|.
+ // The maximum number of rows needed in the buffer is |max_y_filter_size|
+ // (we only need to store enough rows for the biggest filter).
+ //
+ // We use the |first_input_row| to compute the coordinates of all of the
+ // following rows returned by Advance().
+ CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+ int first_input_row)
+ : row_byte_width_(dest_row_pixel_width * 4),
+ num_rows_(max_y_filter_size),
+ next_row_(0),
+ next_row_coordinate_(first_input_row) {
+ buffer_.resize(row_byte_width_ * max_y_filter_size);
+ row_addresses_.resize(num_rows_);
+ }
+
+ // Moves to the next row in the buffer, returning a pointer to the beginning
+ // of it.
+ uint8* AdvanceRow() {
+ uint8* row = &buffer_[next_row_ * row_byte_width_];
+ next_row_coordinate_++;
+
+ // Set the pointer to the next row to use, wrapping around if necessary.
+ next_row_++;
+ if (next_row_ == num_rows_)
+ next_row_ = 0;
+ return row;
+ }
+
+ // Returns a pointer to an "unrolled" array of rows. These rows will start
+ // at the y coordinate placed into |*first_row_index| and will continue in
+ // order for the maximum number of rows in this circular buffer.
+ //
+ // The |first_row_index_| may be negative. This means the circular buffer
+ // starts before the top of the image (it hasn't been filled yet).
+ uint8* const* GetRowAddresses(int* first_row_index) {
+ // Example for a 4-element circular buffer holding coords 6-9.
+ // Row 0 Coord 8
+ // Row 1 Coord 9
+ // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
+ // Row 3 Coord 7
+ //
+ // The "next" row is also the first (lowest) coordinate. This computation
+ // may yield a negative value, but that's OK, the math will work out
+ // since the user of this buffer will compute the offset relative
+ // to the first_row_index and the negative rows will never be used.
+ *first_row_index = next_row_coordinate_ - num_rows_;
+
+ int cur_row = next_row_;
+ for (int i = 0; i < num_rows_; i++) {
+ row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+ // Advance to the next row, wrapping if nexessary.
+ cur_row++;
+ if (cur_row == num_rows_)
+ cur_row = 0;
+ }
+ return &row_addresses_[0];
+ }
+
+ private:
+ // The buffer storing the rows. They are packed, each one row_byte_width_.
+ std::vector<uint8> buffer_;
+
+ // Number of bytes per row in the |buffer_|.
+ int row_byte_width_;
+
+ // The number of rows available in the buffer.
+ int num_rows_;
+
+ // The next row index we should write into. This wraps around as the
+ // circular buffer is used.
+ int next_row_;
+
+ // The y coordinate of the |next_row_|. This is incremented each time a
+ // new row is appended and does not wrap.
+ int next_row_coordinate_;
+
+ // Buffer used by GetRowAddresses().
+ std::vector<uint8*> row_addresses_;
+};
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+template<bool has_alpha>
+void ConvolveHorizontally(const uint8* src_data,
+ const ConvolusionFilter1D& filter,
+ unsigned char* out_row) {
+ // Loop over each pixel on this row in the output image.
+ int num_values = filter.num_values();
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const int16* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const uint8* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ int32 accum[4] = {0};
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ int16 cur_filter = filter_values[filter_x];
+ accum[0] += cur_filter * row_to_filter[filter_x * 4 + 0];
+ accum[1] += cur_filter * row_to_filter[filter_x * 4 + 1];
+ accum[2] += cur_filter * row_to_filter[filter_x * 4 + 2];
+ if (has_alpha)
+ accum[3] += cur_filter * row_to_filter[filter_x * 4 + 3];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ accum[0] >>= ConvolusionFilter1D::kShiftBits;
+ accum[1] >>= ConvolusionFilter1D::kShiftBits;
+ accum[2] >>= ConvolusionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolusionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[out_x * 4 + 0] = ClampTo8(accum[0]);
+ out_row[out_x * 4 + 1] = ClampTo8(accum[1]);
+ out_row[out_x * 4 + 2] = ClampTo8(accum[2]);
+ if (has_alpha)
+ out_row[out_x * 4 + 3] = ClampTo8(accum[3]);
+ }
+}
+
+// Does vertical convolusion to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically(const int16* filter_values,
+ int filter_length,
+ uint8* const* source_data_rows,
+ int pixel_width,
+ uint8* out_row) {
+ // We go through each column in the output and do a vertical convolusion,
+ // generating one output pixel each time.
+ for (int out_x = 0; out_x < pixel_width; out_x++) {
+ // Compute the number of bytes over in each row that the current column
+ // we're convolving starts at. The pixel will cover the next 4 bytes.
+ int byte_offset = out_x * 4;
+
+ // Apply the filter to one column of pixels.
+ int32 accum[4] = {0};
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ int16 cur_filter = filter_values[filter_y];
+ accum[0] += cur_filter * source_data_rows[filter_y][byte_offset + 0];
+ accum[1] += cur_filter * source_data_rows[filter_y][byte_offset + 1];
+ accum[2] += cur_filter * source_data_rows[filter_y][byte_offset + 2];
+ if (has_alpha)
+ accum[3] += cur_filter * source_data_rows[filter_y][byte_offset + 3];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of precision.
+ accum[0] >>= ConvolusionFilter1D::kShiftBits;
+ accum[1] >>= ConvolusionFilter1D::kShiftBits;
+ accum[2] >>= ConvolusionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolusionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[byte_offset + 0] = ClampTo8(accum[0]);
+ out_row[byte_offset + 1] = ClampTo8(accum[1]);
+ out_row[byte_offset + 2] = ClampTo8(accum[2]);
+ if (has_alpha) {
+ uint8 alpha = ClampTo8(accum[3]);
+
+ // Make sure the alpha channel doesn't come out larger than any of the
+ // color channels. We use premultipled alpha channels, so this should
+ // never happen, but rounding errors will cause this from time to time.
+ // These "impossible" colors will cause overflows (and hence random pixel
+ // values) when the resulting bitmap is drawn to the screen.
+ //
+ // We only need to do this when generating the final output row (here).
+ int max_color_channel = std::max(out_row[byte_offset + 0],
+ std::max(out_row[byte_offset + 1], out_row[byte_offset + 2]));
+ if (alpha < max_color_channel)
+ out_row[byte_offset + 3] = max_color_channel;
+ else
+ out_row[byte_offset + 3] = alpha;
+ } else {
+ // No alpha channel, the image is opqaue.
+ out_row[byte_offset + 3] = 0xff;
+ }
+ }
+}
+
+} // namespace
+
+// ConvolusionFilter1D ---------------------------------------------------------
+
+void ConvolusionFilter1D::AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length) {
+ FilterInstance instance;
+ instance.data_location = static_cast<int>(filter_values_.size());
+ instance.offset = filter_offset;
+ instance.length = filter_length;
+ filters_.push_back(instance);
+
+ DCHECK(filter_length > 0);
+ for (int i = 0; i < filter_length; i++)
+ filter_values_.push_back(FloatToFixed(filter_values[i]));
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+void ConvolusionFilter1D::AddFilter(int filter_offset,
+ const int16* filter_values,
+ int filter_length) {
+ FilterInstance instance;
+ instance.data_location = static_cast<int>(filter_values_.size());
+ instance.offset = filter_offset;
+ instance.length = filter_length;
+ filters_.push_back(instance);
+
+ DCHECK(filter_length > 0);
+ for (int i = 0; i < filter_length; i++)
+ filter_values_.push_back(filter_values[i]);
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+// BGRAConvolve2D -------------------------------------------------------------
+
+void BGRAConvolve2D(const uint8* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolusionFilter1D& filter_x,
+ const ConvolusionFilter1D& filter_y,
+ uint8* output) {
+ int max_y_filter_size = filter_y.max_filter();
+
+ // The next row in the input that we will generate a horizontally
+ // convolved row for. If the filter doesn't start at the beginning of the
+ // image (this is the case when we are only resizing a subset), then we
+ // don't want to generate any output rows before that. Compute the starting
+ // row for convolusion as the first pixel for the first vertical filter.
+ int filter_offset, filter_length;
+ const int16* filter_values =
+ filter_y.FilterForValue(0, &filter_offset, &filter_length);
+ int next_x_row = filter_offset;
+
+ // We loop over each row in the input doing a horizontal convolusion. This
+ // will result in a horizontally convolved image. We write the results into
+ // a circular buffer of convolved rows and do vertical convolusion as rows
+ // are available. This prevents us from having to store the entire
+ // intermediate image and helps cache coherency.
+ CircularRowBuffer row_buffer(filter_x.num_values(), max_y_filter_size,
+ filter_offset);
+
+ // Loop over every possible output row, processing just enough horizontal
+ // convolusions to run each subsequent vertical convolusion.
+ int output_row_byte_width = filter_x.num_values() * 4;
+ int num_output_rows = filter_y.num_values();
+ for (int out_y = 0; out_y < num_output_rows; out_y++) {
+ filter_values = filter_y.FilterForValue(out_y,
+ &filter_offset, &filter_length);
+
+ // Generate output rows until we have enough to run the current filter.
+ while (next_x_row < filter_offset + filter_length) {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ next_x_row++;
+ }
+
+ // Compute where in the output image this row of final data will go.
+ uint8* cur_output_row = &output[out_y * output_row_byte_width];
+
+ // Get the list of rows that the circular buffer has, in order.
+ int first_row_in_circular_buffer;
+ uint8* const* rows_to_convolve =
+ row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+ // Now compute the start of the subset of those rows that the filter
+ // needs.
+ uint8* const* first_row_for_filter =
+ &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+ if (source_has_alpha) {
+ ConvolveVertically<true>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ } else {
+ ConvolveVertically<false>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ }
+ }
+}
+
+} // namespace gfx \ No newline at end of file
diff --git a/base/gfx/convolver.h b/base/gfx/convolver.h
new file mode 100644
index 0000000..8a2310e
--- /dev/null
+++ b/base/gfx/convolver.h
@@ -0,0 +1,156 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_CONVOLVER_H__
+#define BASE_GFX_CONVOLVER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+namespace gfx {
+
+// Represents a filter in one dimension. Each output pixel has one entry in this
+// object for the filter values contributing to it. You build up the filter
+// list by calling AddFilter for each output pixel (in order).
+//
+// We do 2-dimensional convolusion by first convolving each row by one
+// ConvolusionFilter1D, then convolving each column by another one.
+//
+// Entries are stored in fixed point, shifted left by kShiftBits.
+class ConvolusionFilter1D {
+ public:
+ // The number of bits that fixed point values are shifted by.
+ enum { kShiftBits = 14 };
+
+ ConvolusionFilter1D() : max_filter_(0) {
+ }
+
+ // Convert between floating point and our fixed point representation.
+ static inline int16 FloatToFixed(float f) {
+ return static_cast<int16>(f * (1 << kShiftBits));
+ }
+ static inline unsigned char FixedToChar(int16 x) {
+ return static_cast<unsigned char>(x >> kShiftBits);
+ }
+
+ // Returns the maximum pixel span of a filter.
+ int max_filter() const { return max_filter_; }
+
+ // Returns the number of filters in this filter. This is the dimension of the
+ // output image.
+ int num_values() const { return static_cast<int>(filters_.size()); }
+
+ // Appends the given list of scaling values for generating a given output
+ // pixel. |filter_offset| is the distance from the edge of the image to where
+ // the scaling factors start. The scaling factors apply to the source pixels
+ // starting from this position, and going for the next |filter_length| pixels.
+ //
+ // You will probably want to make sure your input is normalized (that is,
+ // all entries in |filter_values| sub to one) to prevent affecting the overall
+ // brighness of the image.
+ //
+ // The filter_length must be > 0.
+ //
+ // This version will automatically convert your input to fixed point.
+ void AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length);
+
+ // Same as the above version, but the input is already fixed point.
+ void AddFilter(int filter_offset,
+ const int16* filter_values,
+ int filter_length);
+
+ // Retrieves a filter for the given |value_offset|, a position in the output
+ // image in the direction we're convolving. The offset and length of the
+ // filter values are put into the corresponding out arguments (see AddFilter
+ // above for what these mean), and a pointer to the first scaling factor is
+ // returned. There will be |filter_length| values in this array.
+ inline const int16* FilterForValue(int value_offset,
+ int* filter_offset,
+ int* filter_length) const {
+ const FilterInstance& filter = filters_[value_offset];
+ *filter_offset = filter.offset;
+ *filter_length = filter.length;
+ return &filter_values_[filter.data_location];
+ }
+
+ private:
+ struct FilterInstance {
+ // Offset within filter_values for this instance of the filter.
+ int data_location;
+
+ // Distance from the left of the filter to the center. IN PIXELS
+ int offset;
+
+ // Number of values in this filter instance.
+ int length;
+ };
+
+ // Stores the information for each filter added to this class.
+ std::vector<FilterInstance> filters_;
+
+ // We store all the filter values in this flat list, indexed by
+ // |FilterInstance.data_location| to avoid the mallocs required for storing
+ // each one separately.
+ std::vector<int16> filter_values_;
+
+ // The maximum size of any filter we've added.
+ int max_filter_;
+};
+
+// Does a two-dimensional convolusion on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+void BGRAConvolve2D(const uint8* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolusionFilter1D& xfilter,
+ const ConvolusionFilter1D& yfilter,
+ uint8* output);
+
+} // namespace gfx
+
+#endif // BASE_GFX_CONVOLVER_H__
diff --git a/base/gfx/convolver_unittest.cc b/base/gfx/convolver_unittest.cc
new file mode 100644
index 0000000..b9d210a
--- /dev/null
+++ b/base/gfx/convolver_unittest.cc
@@ -0,0 +1,151 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string.h>
+#include <time.h>
+#include <vector>
+
+#include "base/gfx/convolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace gfx {
+
+namespace {
+
+// Fills the given filter with impulse functions for the range 0->num_entries.
+ void FillImpulseFilter(int num_entries, ConvolusionFilter1D* filter) {
+ float one = 1.0f;
+ for (int i = 0; i < num_entries; i++)
+ filter->AddFilter(i, &one, 1);
+}
+
+// Filters the given input with the impulse function, and verifies that it
+// does not change.
+void TestImpulseConvolusion(const unsigned char* data, int width, int height) {
+ int byte_count = width * height * 4;
+
+ ConvolusionFilter1D filter_x;
+ FillImpulseFilter(width, &filter_x);
+
+ ConvolusionFilter1D filter_y;
+ FillImpulseFilter(height, &filter_y);
+
+ std::vector<unsigned char> output;
+ output.resize(byte_count);
+ BGRAConvolve2D(data, width * 4, true, filter_x, filter_y, &output[0]);
+
+ // Output should exactly match input.
+ EXPECT_EQ(0, memcmp(data, &output[0], byte_count));
+}
+
+// Fills the destination filter with a box filter averaging every two pixels
+// to produce the output.
+void FillBoxFilter(int size, ConvolusionFilter1D* filter) {
+ const float box[2] = { 0.5, 0.5 };
+ for (int i = 0; i < size; i++)
+ filter->AddFilter(i * 2, box, 2);
+}
+
+} // namespace
+
+// Tests that each pixel, when set and run through the impulse filter, does
+// not change.
+TEST(Convolver, Impulse) {
+ // We pick an "odd" size that is not likely to fit on any boundaries so that
+ // we can see if all the widths and paddings are handled properly.
+ int width = 15;
+ int height = 31;
+ int byte_count = width * height * 4;
+ std::vector<unsigned char> input;
+ input.resize(byte_count);
+
+ unsigned char* input_ptr = &input[0];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ for (int channel = 0; channel < 3; channel++) {
+ memset(input_ptr, 0, byte_count);
+ input_ptr[(y * width + x) * 4 + channel] = 0xff;
+ // Always set the alpha channel or it will attempt to "fix" it for us.
+ input_ptr[(y * width + x) * 4 + 3] = 0xff;
+ TestImpulseConvolusion(input_ptr, width, height);
+ }
+ }
+ }
+}
+
+// Tests that using a box filter to halve an image results in every square of 4
+// pixels in the original get averaged to a pixel in the output.
+TEST(Convolver, Halve) {
+ static const int kSize = 16;
+
+ int src_width = kSize;
+ int src_height = kSize;
+ int src_row_stride = src_width * 4;
+ int src_byte_count = src_row_stride * src_height;
+ std::vector<unsigned char> input;
+ input.resize(src_byte_count);
+
+ int dest_width = src_width / 2;
+ int dest_height = src_height / 2;
+ int dest_byte_count = dest_width * dest_height * 4;
+ std::vector<unsigned char> output;
+ output.resize(dest_byte_count);
+
+ // First fill the array with a bunch of random data.
+ srand(static_cast<unsigned>(time(NULL)));
+ for (int i = 0; i < src_byte_count; i++)
+ input[i] = rand() * 255 / RAND_MAX;
+
+ // Compute the filters.
+ ConvolusionFilter1D filter_x, filter_y;
+ FillBoxFilter(dest_width, &filter_x);
+ FillBoxFilter(dest_height, &filter_y);
+
+ // Do the convolusion.
+ BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y, &output[0]);
+
+ // Compute the expected results and check, allowing for a small difference
+ // to account for rounding errors.
+ for (int y = 0; y < dest_height; y++) {
+ for (int x = 0; x < dest_width; x++) {
+ for (int channel = 0; channel < 4; channel++) {
+ int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel;
+ int value = input[src_offset] + // Top left source pixel.
+ input[src_offset + 4] + // Top right source pixel.
+ input[src_offset + src_row_stride] + // Lower left.
+ input[src_offset + src_row_stride + 4]; // Lower right.
+ value /= 4; // Average.
+ int difference = value - output[(y * dest_width + x) * 4 + channel];
+ EXPECT_TRUE(difference >= -1 || difference <= 1);
+ }
+ }
+ }
+}
+
+} // namespace gfx \ No newline at end of file
diff --git a/base/gfx/font_utils.cc b/base/gfx/font_utils.cc
new file mode 100644
index 0000000..340dbbc
--- /dev/null
+++ b/base/gfx/font_utils.cc
@@ -0,0 +1,305 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/font_utils.h"
+
+#include <limits>
+#include <map>
+
+#include "base/gfx/uniscribe.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "base/string_util.h"
+#include "unicode/locid.h"
+
+namespace gfx {
+
+namespace {
+
+// hash_map has extra cost with no sizable gain for a small number of integer
+// key items. When the map size becomes much bigger (which will be later as
+// more scripts are added) and this turns out to be prominent in the profile, we
+// may consider switching to hash_map (or just an array if we support all the
+// scripts)
+typedef std::map<UScriptCode, const wchar_t*> ScriptToFontMap;
+
+struct ScriptToFontMapSingletonTraits
+ : public DefaultSingletonTraits<ScriptToFontMap> {
+ static ScriptToFontMap* New() {
+ struct FontMap {
+ UScriptCode script;
+ const wchar_t* family;
+ };
+
+ const static FontMap font_map[] = {
+ {USCRIPT_SIMPLIFIED_HAN, L"simsun"},
+ {USCRIPT_TRADITIONAL_HAN, L"pmingliu"},
+ {USCRIPT_HIRAGANA, L"ms pgothic"},
+ {USCRIPT_KATAKANA, L"ms pgothic"},
+ {USCRIPT_KATAKANA_OR_HIRAGANA, L"ms pgothic"},
+ {USCRIPT_HANGUL, L"gulim"},
+ {USCRIPT_THAI, L"tahoma"},
+ {USCRIPT_HEBREW, L"david"},
+ {USCRIPT_ARABIC, L"simplified arabic"},
+ {USCRIPT_DEVANAGARI, L"mangal"},
+ {USCRIPT_BENGALI, L"vrinda"},
+ {USCRIPT_GURMUKHI, L"raavi"},
+ {USCRIPT_GUJARATI, L"shruti"},
+ {USCRIPT_ORIYA, L"kalinga"},
+ {USCRIPT_TAMIL, L"latha"},
+ {USCRIPT_TELUGU, L"gautami"},
+ {USCRIPT_KANNADA, L"tunga"},
+ {USCRIPT_MALAYALAM, L"kartika"},
+ {USCRIPT_LAO, L"dokchampa"},
+ {USCRIPT_TIBETAN, L"microsoft himalaya"},
+ {USCRIPT_GEORGIAN, L"sylfaen"},
+ {USCRIPT_ARMENIAN, L"sylfaen"},
+ {USCRIPT_ETHIOPIC, L"nyala"},
+ {USCRIPT_CANADIAN_ABORIGINAL, L"euphemia"},
+ {USCRIPT_CHEROKEE, L"plantagenet cherokee"},
+ {USCRIPT_YI, L"microsoft yi balti"},
+ {USCRIPT_SINHALA, L"iskoola pota"},
+ {USCRIPT_SYRIAC, L"estrangelo edessa"},
+ {USCRIPT_KHMER, L"daunpenh"},
+ {USCRIPT_THAANA, L"mv boli"},
+ {USCRIPT_MONGOLIAN, L"mongolian balti"},
+ // For common, perhaps we should return a font
+ // for the current application/system locale.
+ //{USCRIPT_COMMON, L"times new roman"}
+ };
+
+ ScriptToFontMap* new_instance = new ScriptToFontMap;
+ // Cannot recover from OOM so that there's no need to check.
+ for (int i = 0; i < arraysize(font_map); ++i)
+ (*new_instance)[font_map[i].script] = font_map[i].family;
+
+ // Initialize the locale-dependent mapping.
+ // Since Chrome synchronizes the ICU default locale with its UI locale,
+ // this ICU locale tells the current UI locale of Chrome.
+ Locale locale = Locale::getDefault();
+ ScriptToFontMap::const_iterator iter;
+ if (locale == Locale::getKorean()) {
+ iter = new_instance->find(USCRIPT_HANGUL);
+ } else if (locale == Locale::getJapanese()) {
+ iter = new_instance->find(USCRIPT_KATAKANA_OR_HIRAGANA);
+ } else if (locale == Locale::getTraditionalChinese()) {
+ iter = new_instance->find(USCRIPT_TRADITIONAL_HAN);
+ } else {
+ iter = new_instance->find(USCRIPT_SIMPLIFIED_HAN);
+ }
+ if (iter != new_instance->end())
+ (*new_instance)[USCRIPT_HAN] = iter->second;
+
+ return new_instance;
+ }
+};
+
+Singleton<ScriptToFontMap, ScriptToFontMapSingletonTraits> script_font_map;
+
+const int kUndefinedAscent = std::numeric_limits<int>::min();
+
+// Given an HFONT, return the ascent. If GetTextMetrics fails,
+// kUndefinedAscent is returned, instead.
+int GetAscent(HFONT hfont) {
+ HDC dc = GetDC(NULL);
+ HGDIOBJ oldFont = SelectObject(dc, hfont);
+ TEXTMETRIC tm;
+ BOOL got_metrics = GetTextMetrics(dc, &tm);
+ SelectObject(dc, oldFont);
+ ReleaseDC(NULL, dc);
+ return got_metrics ? tm.tmAscent : kUndefinedAscent;
+}
+
+struct FontData {
+ FontData() : hfont(NULL), ascent(kUndefinedAscent), script_cache(NULL) {}
+ HFONT hfont;
+ int ascent;
+ mutable SCRIPT_CACHE script_cache;
+};
+
+// Again, using hash_map does not earn us much here.
+// page_cycler_test intl2 gave us a 'better' result with map than with hash_map
+// even though they're well-within 1-sigma of each other so that the difference
+// is not significant. On the other hand, some pages in intl2 seem to
+// take longer to load with map in the 1st pass. Need to experiment further.
+typedef std::map<std::wstring, FontData*> FontDataCache;
+struct FontDataCacheSingletonTraits
+ : public DefaultSingletonTraits<FontDataCache> {
+ static void Delete(FontDataCache* cache) {
+ FontDataCache::iterator iter = cache->begin();
+ while (iter != cache->end()) {
+ SCRIPT_CACHE script_cache = iter->second->script_cache;
+ if (script_cache)
+ ScriptFreeCache(&script_cache);
+ delete iter->second;
+ ++iter;
+ }
+ delete cache;
+ }
+};
+
+} // namespace
+
+// TODO(jungshik) : this is font fallback code version 0.1
+// - Cover all the scripts
+// - Get the default font for each script/generic family from the
+// preference instead of hardcoding in the source.
+// - Support generic families (from FontDescription)
+// - If the default font for a script is not available,
+// use EnumFontFamilies or similar APIs to come up with a list of
+// fonts supporting the script and cache the result.
+// - Consider using UnicodeSet (or UnicodeMap) to keep track of which
+// character is supported by which font
+// - Update script_font_cache in response to WM_FONTCHANGE
+
+const wchar_t* GetFontFamilyForScript(UScriptCode script,
+ GenericFamilyType generic) {
+ ScriptToFontMap::const_iterator iter = script_font_map->find(script);
+ const wchar_t* family = NULL;
+ if (iter != script_font_map->end()) {
+ family = iter->second;
+ }
+ return family;
+}
+
+// TODO(jungshik)
+// - Handle 'Inherited', 'Common' and 'Unknown'
+// (see http://www.unicode.org/reports/tr24/#Usage_Model )
+// For 'Inherited' and 'Common', perhaps we need to
+// accept another parameter indicating the previous family
+// and just return it.
+// - All the characters (or characters up to the point a single
+// font can cover) need to be taken into account
+const wchar_t* GetFallbackFamily(const wchar_t* characters,
+ int length,
+ GenericFamilyType generic) {
+ DCHECK(characters && characters[0] && length > 0);
+ UScriptCode script = USCRIPT_COMMON;
+
+ // Sometimes characters common to script (e.g. space) is at
+ // the beginning of a string so that we need to skip them
+ // to get a font required to render the string.
+ int i = 0;
+ UChar32 ucs4 = 0;
+ while (i < length && script == USCRIPT_COMMON ||
+ script == USCRIPT_INVALID_CODE) {
+ U16_NEXT(characters, i, length, ucs4);
+ UErrorCode err = U_ZERO_ERROR;
+ script = uscript_getScript(ucs4, &err);
+ // silently ignore the error
+ }
+
+ // hack for full width ASCII. Japanese are most fond of full-width ASCII.
+ // TODO(jungshik) find a better way !
+ if (0xFF00 < ucs4 && ucs4 < 0xFF5F)
+ return L"ms pgothic";
+
+ const wchar_t* family = GetFontFamilyForScript(script, generic);
+ if (!family) {
+ int plane = ucs4 >> 16;
+ switch (plane) {
+ case 1:
+ family = L"code2001";
+ break;
+ case 2:
+ family = L"simsun ext b";
+ break;
+ default:
+ family = L"arial unicode ms";
+ }
+ }
+
+ return family;
+}
+
+
+
+// Be aware that this is not thread-safe.
+bool GetDerivedFontData(const wchar_t *family,
+ int style,
+ LOGFONT *logfont,
+ int *ascent,
+ HFONT *hfont,
+ SCRIPT_CACHE **script_cache) {
+ DCHECK(logfont && family && *family);
+ // Using |Singleton| here is not free, but the intl2 page cycler test
+ // does not show any noticeable difference with and without it. Leaking
+ // the contents of FontDataCache (especially SCRIPT_CACHE) at the end
+ // of a renderer process may not be a good idea. We may use
+ // atexit(). However, with no noticeable performance difference, |Singleton|
+ // is cleaner, I believe.
+ FontDataCache* font_data_cache =
+ Singleton<FontDataCache, FontDataCacheSingletonTraits>::get();
+ // TODO(jungshik) : This comes up pretty high in the profile so that
+ // we need to measure whether using SHA256 (after coercing all the
+ // fields to char*) is faster than StringPrintf.
+ std::wstring font_key = StringPrintf(L"%1d:%d:%s", style, logfont->lfHeight,
+ family);
+ FontDataCache::const_iterator iter = font_data_cache->find(font_key);
+ FontData *derived;
+ if (iter == font_data_cache->end()) {
+ DCHECK(wcslen(family) < LF_FACESIZE);
+ wcscpy_s(logfont->lfFaceName, LF_FACESIZE, family);
+ // TODO(jungshik): CreateFontIndirect always comes up with
+ // a font even if there's no font matching the name. Need to
+ // check it against what we actually want (as is done in FontCacheWin.cpp)
+ derived = new FontData;
+ derived->hfont = CreateFontIndirect(logfont);
+ // GetAscent may return kUndefinedAscent, but we still want to
+ // cache it so that we won't have to call CreateFontIndirect once
+ // more for HFONT next time.
+ derived->ascent = GetAscent(derived->hfont);
+ (*font_data_cache)[font_key] = derived;
+ } else {
+ derived = iter->second;
+ // Last time, GetAscent failed so that only HFONT was
+ // cached. Try once more assuming that TryPreloadFont
+ // was called by a caller between calls.
+ if (kUndefinedAscent == derived->ascent)
+ derived->ascent = GetAscent(derived->hfont);
+ }
+ *hfont = derived->hfont;
+ *ascent = derived->ascent;
+ *script_cache = &(derived->script_cache);
+ return *ascent != kUndefinedAscent;
+}
+
+int GetStyleFromLogfont(const LOGFONT* logfont) {
+ // TODO(jungshik) : consider defining UNDEFINED or INVALID for style and
+ // returning it when logfont is NULL
+ if (!logfont) {
+ NOTREACHED();
+ return FONT_STYLE_NORMAL;
+ }
+ return (logfont->lfItalic ? FONT_STYLE_ITALIC : FONT_STYLE_NORMAL) |
+ (logfont->lfUnderline ? FONT_STYLE_UNDERLINED : FONT_STYLE_NORMAL) |
+ (logfont->lfWeight >= 700 ? FONT_STYLE_BOLD : FONT_STYLE_NORMAL);
+}
+
+} // namespace gfx
diff --git a/base/gfx/font_utils.h b/base/gfx/font_utils.h
new file mode 100644
index 0000000..e06b588
--- /dev/null
+++ b/base/gfx/font_utils.h
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A collection of utilities for font handling.
+
+#ifndef BASE_GFX_FONT_UTILS_H__
+#define BASE_GFX_FONT_UTILS_H__
+
+#include <usp10.h>
+#include <wchar.h>
+#include <windows.h>
+
+#include <unicode/uscript.h>
+
+namespace gfx {
+
+// The order of family types needs to be exactly the same as
+// WebCore::FontDescription::GenericFamilyType. We may lift that restriction
+// when we make webkit_glue::WebkitGenericToChromeGenericFamily more
+// intelligent.
+enum GenericFamilyType {
+ GENERIC_FAMILY_NONE = 0,
+ GENERIC_FAMILY_STANDARD,
+ GENERIC_FAMILY_SERIF,
+ GENERIC_FAMILY_SANSSERIF,
+ GENERIC_FAMILY_MONOSPACE,
+ GENERIC_FAMILY_CURSIVE,
+ GENERIC_FAMILY_FANTASY
+};
+
+// Return a font family that supports a script and belongs to |generic| font family.
+// It can retun NULL and a caller has to implement its own fallback.
+const wchar_t* GetFontFamilyForScript(UScriptCode script,
+ GenericFamilyType generic);
+
+// Return a font family that can render |characters| based on
+// what script characters belong to.
+const wchar_t* GetFallbackFamily(const wchar_t *characters, int length,
+ GenericFamilyType generic);
+
+// Derive a new HFONT by replacing lfFaceName of LOGFONT with |family|,
+// calculate the ascent for the derived HFONT, and initialize SCRIPT_CACHE
+// in FontData.
+// |style| is only used for cache key generation. |style| is
+// bit-wise OR of BOLD(1), UNDERLINED(2) and ITALIC(4) and
+// should match what's contained in LOGFONT. It should be calculated
+// by calling GetStyleFromLogFont.
+// Returns false if the font is not accessible, in which case |ascent| field
+// of |fontdata| is set to kUndefinedAscent.
+// Be aware that this is not thread-safe.
+// TODO(jungshik): Instead of having three out params, we'd better have one
+// (|*FontData|), but somehow it mysteriously messes up the layout for
+// certain complex script pages (e.g. hi.wikipedia.org) and also crashes
+// at the start-up if recently visited page list includes pages with complex
+// scripts in their title. Moreover, somehow the very first-pass of
+// intl2 page-cycler test is noticeably slower with one out param than
+// the current version although the subsequent 9 passes take about the
+// same time.
+bool GetDerivedFontData(const wchar_t *family,
+ int style,
+ LOGFONT *logfont,
+ int *ascent,
+ HFONT *hfont,
+ SCRIPT_CACHE **script_cache);
+
+enum {
+ FONT_STYLE_NORMAL = 0,
+ FONT_STYLE_BOLD = 1,
+ FONT_STYLE_ITALIC = 2,
+ FONT_STYLE_UNDERLINED = 4
+};
+
+// Derive style (bit-wise OR of FONT_STYLE_BOLD, FONT_STYLE_UNDERLINED, and
+// FONT_STYLE_ITALIC) from LOGFONT. Returns 0 if |*logfont| is NULL.
+int GetStyleFromLogfont(const LOGFONT *logfont);
+
+} // namespace gfx
+
+#endif // BASE_GFX_FONT_UTILS_H__
diff --git a/base/gfx/image_operations.cc b/base/gfx/image_operations.cc
new file mode 100644
index 0000000..4dde08c
--- /dev/null
+++ b/base/gfx/image_operations.cc
@@ -0,0 +1,387 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+#define _USE_MATH_DEFINES
+#include <cmath>
+#include <limits>
+#include <vector>
+
+#include "base/gfx/image_operations.h"
+
+#include "base/gfx/convolver.h"
+#include "base/gfx/rect.h"
+#include "base/gfx/size.h"
+#include "base/logging.h"
+#include "base/stack_container.h"
+#include "SkBitmap.h"
+
+namespace gfx {
+
+namespace {
+
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+ return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+ return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+float EvalBox(float x) {
+ return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+// lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+// sinc(x) = sin(pi*x) / (pi*x);
+float EvalLanczos(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the discontinuity at the origin.
+ float xpi = x * static_cast<float>(M_PI);
+ return (sin(xpi) / xpi) * // sinc(x)
+ sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+class ResizeFilter {
+ public:
+ ResizeFilter(ImageOperations::ResizeMethod method,
+ const Size& src_full_size,
+ const Size& dest_size,
+ const Rect& dest_subset);
+
+ // Returns the bounds in the input bitmap of data that is used in the output.
+ // The filter offsets are within this rectangle.
+ const Rect& src_depend() { return src_depend_; }
+
+ // Returns the filled filter values.
+ const ConvolusionFilter1D& x_filter() { return x_filter_; }
+ const ConvolusionFilter1D& y_filter() { return y_filter_; }
+
+ private:
+ // Returns the number of pixels that the filer spans, in filter space (the
+ // destination image).
+ float GetFilterSupport(float scale) {
+ switch (method_) {
+ case ImageOperations::RESIZE_BOX:
+ // The box filter just scales with the image scaling.
+ return 0.5f; // Only want one side of the filter = /2.
+ case ImageOperations::RESIZE_LANCZOS3:
+ // The lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 3 for Lanczos3.
+ return 3.0f;
+ default:
+ NOTREACHED();
+ return 1.0f;
+ }
+ }
+
+ // Computes one set of filters either horizontally or vertically. The caller
+ // will specify the "min" and "max" rather than the bottom/top and
+ // right/bottom so that the same code can be re-used in each dimension.
+ //
+ // |src_depend_lo| and |src_depend_size| gives the range for the source
+ // depend rectangle (horizontally or vertically at the caller's discretion
+ // -- see above for what this means).
+ //
+ // Likewise, the range of destination values to compute and the scale factor
+ // for the transform is also specified.
+ void ComputeFilters(int src_size,
+ int dest_subset_lo, int dest_subset_size,
+ float scale, float src_support,
+ ConvolusionFilter1D* output);
+
+ // Computes the filter value given the coordinate in filter space.
+ inline float ComputeFilter(float pos) {
+ switch (method_) {
+ case ImageOperations::RESIZE_BOX:
+ return EvalBox(pos);
+ case ImageOperations::RESIZE_LANCZOS3:
+ return EvalLanczos(3, pos);
+ default:
+ NOTREACHED();
+ return 0;
+ }
+ }
+
+ ImageOperations::ResizeMethod method_;
+
+ // Subset of source the filters will touch.
+ Rect src_depend_;
+
+ // Size of the filter support on one side only in the destination space.
+ // See GetFilterSupport.
+ float x_filter_support_;
+ float y_filter_support_;
+
+ // Subset of scaled destination bitmap to compute.
+ Rect out_bounds_;
+
+ ConvolusionFilter1D x_filter_;
+ ConvolusionFilter1D y_filter_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ResizeFilter);
+};
+
+ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
+ const Size& src_full_size,
+ const Size& dest_size,
+ const Rect& dest_subset)
+ : method_(method),
+ out_bounds_(dest_subset) {
+ float scale_x = static_cast<float>(dest_size.width()) /
+ static_cast<float>(src_full_size.width());
+ float scale_y = static_cast<float>(dest_size.height()) /
+ static_cast<float>(src_full_size.height());
+
+ x_filter_support_ = GetFilterSupport(scale_x);
+ y_filter_support_ = GetFilterSupport(scale_y);
+
+ gfx::Rect src_full(0, 0, src_full_size.width(), src_full_size.height());
+ gfx::Rect dest_full(0, 0,
+ static_cast<int>(src_full_size.width() * scale_x + 0.5),
+ static_cast<int>(src_full_size.height() * scale_y + 0.5));
+
+ // Support of the filter in source space.
+ float src_x_support = x_filter_support_ / scale_x;
+ float src_y_support = y_filter_support_ / scale_y;
+
+ ComputeFilters(src_full_size.width(), dest_subset.x(), dest_subset.width(),
+ scale_x, src_x_support, &x_filter_);
+ ComputeFilters(src_full_size.height(), dest_subset.y(), dest_subset.height(),
+ scale_y, src_y_support, &y_filter_);
+}
+
+void ResizeFilter::ComputeFilters(int src_size,
+ int dest_subset_lo, int dest_subset_size,
+ float scale, float src_support,
+ ConvolusionFilter1D* output) {
+ int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi)
+
+ // When we're doing a magnification, the scale will be larger than one. This
+ // means the destination pixels are much smaller than the source pixels, and
+ // that the range covered by the filter won't necessarily cover any source
+ // pixel boundaries. Therefore, we use these clamped values (max of 1) for
+ // some computations.
+ float clamped_src_support = std::min(1.0f, src_support);
+ float clamped_scale = std::min(1.0f, scale);
+
+ // Speed up the divisions below by turning them into multiplies.
+ float inv_scale = 1.0f / scale;
+
+ StackVector<float, 64> filter_values;
+ StackVector<int16, 64> fixed_filter_values;
+
+ // Loop over all pixels in the output range. We will generate one set of
+ // filter values for each one. Those values will tell us how to blend the
+ // source pixels to compute the destination pixel.
+ for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
+ dest_subset_i++) {
+ // Reset the arrays. We don't declare them inside so they can re-use the
+ // same malloc-ed buffer.
+ filter_values->clear();
+ fixed_filter_values->clear();
+
+ // This is the pixel in the source directly under the pixel in the dest.
+ float src_pixel = dest_subset_i * inv_scale;
+
+ // Compute the (inclusive) range of source pixels the filter covers.
+ int src_begin = std::max(0, FloorInt(src_pixel - src_support));
+ int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
+
+ // Compute the unnormalized filter value at each location of the source
+ // it covers.
+ float filter_sum = 0.0f; // Sub of the filter values for normalizing.
+ for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
+ cur_filter_pixel++) {
+ // Distance from the center of the filter, this is the filter coordinate
+ // in source space.
+ float src_filter_pos = cur_filter_pixel - src_pixel;
+
+ // Since the filter really exists in dest space, map it there.
+ float dest_filter_pos = src_filter_pos * clamped_scale;
+
+ // Compute the filter value at that location.
+ float filter_value = ComputeFilter(dest_filter_pos);
+ filter_values->push_back(filter_value);
+
+ filter_sum += filter_value;
+ }
+ DCHECK(!filter_values->empty()) << "We should always get a filter!";
+
+ // The filter must be normalized so that we don't affect the brightness of
+ // the image. Convert to normalized fixed point.
+ int16 fixed_sum = 0;
+ for (size_t i = 0; i < filter_values->size(); i++) {
+ int16 cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
+ fixed_sum += cur_fixed;
+ fixed_filter_values->push_back(cur_fixed);
+ }
+
+ // The conversion to fixed point will leave some rounding errors, which
+ // we add back in to avoid affecting the brightness of the image. We
+ // arbitrarily add this to the center of the filter array (this won't always
+ // be the center of the filter function since it could get clipped on the
+ // edges, but it doesn't matter enough to worry about that case).
+ int16 leftovers = output->FloatToFixed(1.0f) - fixed_sum;
+ fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
+
+ // Now it's ready to go.
+ output->AddFilter(src_begin, &fixed_filter_values[0],
+ static_cast<int>(fixed_filter_values->size()));
+ }
+}
+
+} // namespace
+
+// Resize ----------------------------------------------------------------------
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ const Size& dest_size,
+ const Rect& dest_subset) {
+ DCHECK(Rect(dest_size.width(), dest_size.height()).Contains(dest_subset)) <<
+ "The supplied subset does not fall within the destination image.";
+
+ // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
+ // return empty
+ if (source.width() < 1 || source.height() < 1 ||
+ dest_size.width() < 1 || dest_size.height() < 1)
+ return SkBitmap();
+
+ SkAutoLockPixels locker(source);
+
+ ResizeFilter filter(method, Size(source.width(), source.height()),
+ dest_size, dest_subset);
+
+ // Get a source bitmap encompassing this touched area. We construct the
+ // offsets and row strides such that it looks like a new bitmap, while
+ // referring to the old data.
+ const uint8* source_subset =
+ reinterpret_cast<const uint8*>(source.getPixels());
+
+ // Convolve into the result.
+ SkBitmap result;
+ result.setConfig(SkBitmap::kARGB_8888_Config,
+ dest_subset.width(), dest_subset.height());
+ result.allocPixels();
+ BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
+ !source.isOpaque(), filter.x_filter(), filter.y_filter(),
+ static_cast<unsigned char*>(result.getPixels()));
+
+ // Preserve the "opaque" flag for use as an optimization later.
+ result.setIsOpaque(source.isOpaque());
+
+ return result;
+}
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ const Size& dest_size) {
+ Rect dest_subset(0, 0, dest_size.width(), dest_size.height());
+ return Resize(source, method, dest_size, dest_subset);
+}
+
+// static
+SkBitmap ImageOperations::CreateBlendedBitmap(const SkBitmap& first,
+ const SkBitmap& second,
+ double alpha) {
+ DCHECK(alpha <= 1 && alpha >= 0);
+ DCHECK(first.width() == second.width());
+ DCHECK(first.height() == second.height());
+ DCHECK(first.bytesPerPixel() == second.bytesPerPixel());
+ DCHECK(first.config() == SkBitmap::kARGB_8888_Config);
+
+ // Optimize for case where we won't need to blend anything.
+ static const double alpha_min = 1.0 / 255;
+ static const double alpha_max = 254.0 / 255;
+ if (alpha < alpha_min) {
+ return first;
+ } else if (alpha > alpha_max) {
+ return second;
+ }
+
+ SkAutoLockPixels lock_first(first);
+ SkAutoLockPixels lock_second(second);
+
+ SkBitmap blended;
+ blended.setConfig(SkBitmap::kARGB_8888_Config, first.width(),
+ first.height(), 0);
+ blended.allocPixels();
+ blended.eraseARGB(0, 0, 0, 0);
+
+ double first_alpha = 1 - alpha;
+
+ for (int y = 0; y < first.height(); y++) {
+ uint32* first_row = first.getAddr32(0, y);
+ uint32* second_row = second.getAddr32(0, y);
+ uint32* dst_row = blended.getAddr32(0, y);
+
+ for (int x = 0; x < first.width(); x++) {
+ uint32 first_pixel = first_row[x];
+ uint32 second_pixel = second_row[x];
+
+ int a = static_cast<int>(
+ SkColorGetA(first_pixel) * first_alpha +
+ SkColorGetA(second_pixel) * alpha);
+ int r = static_cast<int>(
+ SkColorGetR(first_pixel) * first_alpha +
+ SkColorGetR(second_pixel) * alpha);
+ int g = static_cast<int>(
+ SkColorGetG(first_pixel) * first_alpha +
+ SkColorGetG(second_pixel) * alpha);
+ int b = static_cast<int>(
+ SkColorGetB(first_pixel) * first_alpha +
+ SkColorGetB(second_pixel) * alpha);
+
+ dst_row[x] = SkColorSetARGB(a, r, g, b);
+ }
+ }
+
+ return blended;
+}
+
+} // namespace gfx
diff --git a/base/gfx/image_operations.h b/base/gfx/image_operations.h
new file mode 100644
index 0000000..5d3eeef
--- /dev/null
+++ b/base/gfx/image_operations.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_IMAGE_OPERATIONS_H__
+#define BASE_GFX_IMAGE_OPERATIONS_H__
+
+#include "base/basictypes.h"
+#include "base/gfx/rect.h"
+
+class SkBitmap;
+
+namespace gfx {
+
+class ImageOperations {
+ public:
+ enum ResizeMethod {
+ // Box filter. This is a weighted average of all of the pixels touching
+ // the destination pixel. For enlargement, this is nearest neighbor.
+ //
+ // You probably don't want this, it is here for testing since it is easy to
+ // compute. Use RESIZE_LANCZOS3 instead.
+ RESIZE_BOX,
+
+ // 3-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then oscillates 2 more times. It gives nice sharp edges.
+ RESIZE_LANCZOS3,
+ };
+
+ // Resizes the given source bitmap using the specified resize method, so that
+ // the entire image is (dest_size) big. The dest_subset is the rectangle in
+ // this destination image that should actually be returned.
+ //
+ // The output image will be (dest_subset.width(), dest_subset.height()). This
+ // will save work if you do not need the entire bitmap.
+ //
+ // The destination subset must be smaller than the destination image.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ const Size& dest_size,
+ const Rect& dest_subset);
+
+ // Alternate version for resizing and returning the entire bitmap rather than
+ // a subset.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ const Size& dest_size);
+
+
+ // Create a bitmap that is a blend of two others. The alpha argument
+ // specifies the opacity of the second bitmap. The provided bitmaps must
+ // use have the kARGB_8888_Config config and be of equal dimensions.
+ static SkBitmap CreateBlendedBitmap(const SkBitmap& first,
+ const SkBitmap& second,
+ double alpha);
+ private:
+ ImageOperations(); // Class for scoping only.
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_IMAGE_OPERATIONS_H__
diff --git a/base/gfx/image_operations_unittest.cc b/base/gfx/image_operations_unittest.cc
new file mode 100644
index 0000000..1c2b40f
--- /dev/null
+++ b/base/gfx/image_operations_unittest.cc
@@ -0,0 +1,172 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+
+#include "base/gfx/image_operations.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "SkBitmap.h"
+
+namespace {
+
+// Computes the average pixel value for the given range, inclusive.
+uint32_t AveragePixel(const SkBitmap& bmp,
+ int x_min, int x_max,
+ int y_min, int y_max) {
+ float accum[4] = {0, 0, 0, 0};
+ int count = 0;
+ for (int y = y_min; y <= y_max; y++) {
+ for (int x = x_min; x <= x_max; x++) {
+ uint32_t cur = *bmp.getAddr32(x, y);
+ accum[0] += SkColorGetB(cur);
+ accum[1] += SkColorGetG(cur);
+ accum[2] += SkColorGetR(cur);
+ accum[3] += SkColorGetA(cur);
+ count++;
+ }
+ }
+
+ return SkColorSetARGB(static_cast<unsigned char>(accum[3] / count),
+ static_cast<unsigned char>(accum[2] / count),
+ static_cast<unsigned char>(accum[1] / count),
+ static_cast<unsigned char>(accum[0] / count));
+}
+
+// Returns true if each channel of the given two colors are "close." This is
+// used for comparing colors where rounding errors may cause off-by-one.
+bool ColorsClose(uint32_t a, uint32_t b) {
+ return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
+}
+
+void FillDataToBitmap(int w, int h, SkBitmap* bmp) {
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ unsigned char* src_data =
+ reinterpret_cast<unsigned char*>(bmp->getAddr32(0, 0));
+ for (int i = 0; i < w * h; i++) {
+ src_data[i * 4 + 0] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 1] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 2] = static_cast<unsigned char>(i % 255);
+ src_data[i * 4 + 3] = static_cast<unsigned char>(i % 255);
+ }
+}
+
+} // namespace
+
+// Makes the bitmap 50% the size as the original using a box filter. This is
+// an easy operation that we can check the results for manually.
+TEST(ImageOperations, Halve) {
+ // Make our source bitmap.
+ int src_w = 30, src_h = 38;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a halving of the full bitmap.
+ SkBitmap actual_results = gfx::ImageOperations::Resize(
+ src, gfx::ImageOperations::RESIZE_BOX, gfx::Size(src_w / 2, src_h / 2));
+ ASSERT_EQ(src_w / 2, actual_results.width());
+ ASSERT_EQ(src_h / 2, actual_results.height());
+
+ // Compute the expected values & compare.
+ SkAutoLockPixels lock(actual_results);
+ for (int y = 0; y < actual_results.height(); y++) {
+ for (int x = 0; x < actual_results.width(); x++) {
+ int first_x = std::max(0, x * 2 - 1);
+ int last_x = std::min(src_w - 1, x * 2);
+
+ int first_y = std::max(0, y * 2 - 1);
+ int last_y = std::min(src_h - 1, y * 2);
+
+ uint32_t expected_color = AveragePixel(src,
+ first_x, last_x, first_y, last_y);
+ EXPECT_TRUE(ColorsClose(expected_color, *actual_results.getAddr32(x, y)));
+ }
+ }
+}
+
+TEST(ImageOperations, HalveSubset) {
+ // Make our source bitmap.
+ int src_w = 16, src_h = 34;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a halving of the full bitmap.
+ SkBitmap full_results = gfx::ImageOperations::Resize(
+ src, gfx::ImageOperations::RESIZE_BOX, gfx::Size(src_w / 2, src_h / 2));
+ ASSERT_EQ(src_w / 2, full_results.width());
+ ASSERT_EQ(src_h / 2, full_results.height());
+
+ // Now do a halving of a a subset, recall the destination subset is in the
+ // destination coordinate system (max = half of the original image size).
+ gfx::Rect subset_rect(2, 3, 3, 6);
+ SkBitmap subset_results = gfx::ImageOperations::Resize(
+ src, gfx::ImageOperations::RESIZE_BOX,
+ gfx::Size(src_w / 2, src_h / 2), subset_rect);
+ ASSERT_EQ(subset_rect.width(), subset_results.width());
+ ASSERT_EQ(subset_rect.height(), subset_results.height());
+
+ // The computed subset and the corresponding subset of the original image
+ // should be the same.
+ SkAutoLockPixels full_lock(full_results);
+ SkAutoLockPixels subset_lock(subset_results);
+ for (int y = 0; y < subset_rect.height(); y++) {
+ for (int x = 0; x < subset_rect.width(); x++) {
+ ASSERT_EQ(
+ *full_results.getAddr32(x + subset_rect.x(), y + subset_rect.y()),
+ *subset_results.getAddr32(x, y));
+ }
+ }
+}
+
+// Resamples an iamge to the same image, it should give almost the same result.
+TEST(ImageOperations, ResampleToSame) {
+ // Make our source bitmap.
+ int src_w = 16, src_h = 34;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a resize of the full bitmap to the same size. The lanczos filter is good
+ // enough that we should get exactly the same image for output.
+ SkBitmap results = gfx::ImageOperations::Resize(
+ src, gfx::ImageOperations::RESIZE_LANCZOS3, gfx::Size(src_w, src_h));
+ ASSERT_EQ(src_w, results.width());
+ ASSERT_EQ(src_h, results.height());
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels results_lock(results);
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ EXPECT_EQ(*src.getAddr32(x, y), *results.getAddr32(x, y));
+ }
+ }
+} \ No newline at end of file
diff --git a/base/gfx/img_resize_perftest.cc b/base/gfx/img_resize_perftest.cc
new file mode 100644
index 0000000..43f8deb
--- /dev/null
+++ b/base/gfx/img_resize_perftest.cc
@@ -0,0 +1,94 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <time.h>
+
+#include "base/perftimer.h"
+#include "base/gfx/convolver.h"
+#include "base/gfx/image_operations.h"
+#include "base/gfx/image_resizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void FillRandomData(char* dest, int byte_count) {
+ srand(static_cast<unsigned>(time(NULL)));
+ for (int i = 0; i < byte_count; i++)
+ dest[i] = rand() * 255 / RAND_MAX;
+}
+
+} // namespace
+
+// Old code gives [1521, 1519]ms for this, 4000x4000 -> 2100x2100 lanczos8
+
+TEST(ImageResizePerf, BigFilter) {
+ static const int kSrcWidth = 4000;
+ static const int kSrcHeight = 4000;
+ static const int kSrcByteSize = kSrcWidth * kSrcHeight * 4;
+
+ SkBitmap src_bmp;
+ src_bmp.setConfig(SkBitmap::kARGB_8888_Config, kSrcWidth, kSrcHeight);
+ src_bmp.allocPixels();
+ FillRandomData(reinterpret_cast<char*>(src_bmp.getAddr32(0, 0)),
+ kSrcByteSize);
+
+ // Make the dest size > 1/2 so the 50% optimization doesn't kick in.
+ static const int kDestWidth = 1400;
+ static const int kDestHeight = 1400;
+
+ PerfTimeLogger resize_timer("resize");
+ gfx::ImageResizer resizer(gfx::ImageResizer::LANCZOS3);
+ SkBitmap dest = resizer.Resize(src_bmp, kDestWidth, kDestHeight);
+}
+
+// The original image filter we were using took 523ms for this test, while this
+// one takes 857ms.
+// TODO(brettw) make this at least 64% faster.
+TEST(ImageOperationPerf, BigFilter) {
+ static const int kSrcWidth = 4000;
+ static const int kSrcHeight = 4000;
+ static const int kSrcByteSize = kSrcWidth * kSrcHeight * 4;
+
+ SkBitmap src_bmp;
+ src_bmp.setConfig(SkBitmap::kARGB_8888_Config, kSrcWidth, kSrcHeight);
+ src_bmp.allocPixels();
+ src_bmp.setIsOpaque(true);
+ FillRandomData(reinterpret_cast<char*>(src_bmp.getAddr32(0, 0)),
+ kSrcByteSize);
+
+ // Make the dest size > 1/2 so the 50% optimization doesn't kick in.
+ static const int kDestWidth = 1400;
+ static const int kDestHeight = 1400;
+
+ PerfTimeLogger resize_timer("resize");
+ SkBitmap dest = gfx::ImageOperations::Resize(src_bmp,
+ gfx::ImageOperations::RESIZE_LANCZOS3, (float)kDestWidth / (float)kSrcWidth,
+ (float)kDestHeight / (float)kSrcHeight);
+}
diff --git a/base/gfx/native_theme.cc b/base/gfx/native_theme.cc
new file mode 100644
index 0000000..8ab4ca3
--- /dev/null
+++ b/base/gfx/native_theme.cc
@@ -0,0 +1,626 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/native_theme.h"
+
+#include <windows.h>
+#include <uxtheme.h>
+#include <vsstyle.h>
+#include <vssym32.h>
+
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/skia_utils.h"
+#include "base/gfx/rect.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "skia/include/SkShader.h"
+
+namespace gfx {
+
+/* static */
+const NativeTheme* NativeTheme::instance() {
+ // The global NativeTheme instance.
+ static const NativeTheme s_native_theme;
+ return &s_native_theme;
+}
+
+NativeTheme::NativeTheme()
+ : theme_dll_(LoadLibrary(L"uxtheme.dll")),
+ draw_theme_(NULL),
+ draw_theme_ex_(NULL),
+ get_theme_color_(NULL),
+ get_theme_content_rect_(NULL),
+ get_theme_part_size_(NULL),
+ open_theme_(NULL),
+ close_theme_(NULL),
+ set_theme_properties_(NULL),
+ is_theme_active_(NULL),
+ get_theme_int_(NULL) {
+ if (theme_dll_) {
+ draw_theme_ = reinterpret_cast<DrawThemeBackgroundPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackground"));
+ draw_theme_ex_ = reinterpret_cast<DrawThemeBackgroundExPtr>(
+ GetProcAddress(theme_dll_, "DrawThemeBackgroundEx"));
+ get_theme_color_ = reinterpret_cast<GetThemeColorPtr>(
+ GetProcAddress(theme_dll_, "GetThemeColor"));
+ get_theme_content_rect_ = reinterpret_cast<GetThemeContentRectPtr>(
+ GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect"));
+ get_theme_part_size_ = reinterpret_cast<GetThemePartSizePtr>(
+ GetProcAddress(theme_dll_, "GetThemePartSize"));
+ open_theme_ = reinterpret_cast<OpenThemeDataPtr>(
+ GetProcAddress(theme_dll_, "OpenThemeData"));
+ close_theme_ = reinterpret_cast<CloseThemeDataPtr>(
+ GetProcAddress(theme_dll_, "CloseThemeData"));
+ set_theme_properties_ = reinterpret_cast<SetThemeAppPropertiesPtr>(
+ GetProcAddress(theme_dll_, "SetThemeAppProperties"));
+ is_theme_active_ = reinterpret_cast<IsThemeActivePtr>(
+ GetProcAddress(theme_dll_, "IsThemeActive"));
+ get_theme_int_ = reinterpret_cast<GetThemeIntPtr>(
+ GetProcAddress(theme_dll_, "GetThemeInt"));
+ }
+ memset(theme_handles_, 0, sizeof(theme_handles_));
+}
+
+NativeTheme::~NativeTheme() {
+ if (theme_dll_) {
+ CloseHandles();
+ FreeLibrary(theme_dll_);
+ }
+}
+
+HRESULT NativeTheme::PaintButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(BUTTON);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ // All pressed states have both low bits set, and no other states do.
+ const bool focused = ((state_id & PBS_PRESSED) == PBS_PRESSED);
+ if ((part_id == BP_PUSHBUTTON) && focused) {
+ // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the
+ // button itself is shrunk by 1 pixel.
+ HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW);
+ if (brush) {
+ FrameRect(hdc, rect, brush);
+ InflateRect(rect, -1, -1);
+ }
+ }
+
+ DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state);
+
+ // BP_RADIOBUTTON, BP_CHECKBOX, BP_GROUPBOX and BP_USERBUTTON have their
+ // focus drawn over the control.
+ if ((part_id != BP_PUSHBUTTON) && focused)
+ DrawFocusRect(hdc, rect);
+
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const {
+ // TODO(ojan): http://b/1210017 Figure out how to give the ability to
+ // exclude individual edges from being drawn.
+
+ HANDLE handle = GetThemeHandle(TEXTFIELD);
+ // TODO(mpcomplete): can we detect if the color is specified by the user,
+ // and if not, just use the system color?
+ // CreateSolidBrush() accepts a RGB value but alpha must be 0.
+ HBRUSH bg_brush = CreateSolidBrush(color);
+ HRESULT hr;
+ // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible
+ // draw_theme_ex_ is NULL and draw_theme_ is non-null.
+ if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) {
+ if (draw_theme_ex_) {
+ static DTBGOPTS omit_border_options = {
+ sizeof(DTBGOPTS),
+ DTBG_OMITBORDER,
+ {0,0,0,0}
+ };
+ DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options;
+ hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts);
+ } else {
+ hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+
+ // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL.
+ if (fill_content_area && get_theme_content_rect_) {
+ RECT content_rect;
+ hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect,
+ &content_rect);
+ FillRect(hdc, &content_rect, bg_brush);
+ }
+ } else {
+ // Draw it manually.
+ if (draw_edges)
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+ if (fill_content_area) {
+ FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ?
+ reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1) : bg_brush);
+ }
+ hr = S_OK;
+ }
+ DeleteObject(bg_brush);
+ return hr;
+}
+
+HRESULT NativeTheme::PaintMenuList(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENULIST);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL);
+
+ // Draw it manually.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarTrack(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* target_rect,
+ RECT* align_rect,
+ PlatformCanvas* canvas) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL);
+
+ // Draw it manually.
+ const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR);
+ const DWORD color3DFace = GetSysColor(COLOR_3DFACE);
+ if ((colorScrollbar != color3DFace) &&
+ (colorScrollbar != GetSysColor(COLOR_WINDOW))) {
+ FillRect(hdc, target_rect, reinterpret_cast<HBRUSH>(COLOR_SCROLLBAR + 1));
+ } else {
+ // Create a 2x2 checkerboard pattern using the 3D face and highlight
+ // colors.
+ SkColor face = COLORREFToSkColor(color3DFace);
+ SkColor highlight = COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT));
+ SkColor buffer[] = { face, highlight, highlight, face };
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+ bitmap.setPixels(buffer);
+ SkShader* shader = SkShader::CreateBitmapShader(bitmap,
+ SkShader::kRepeat_TileMode,
+ SkShader::kRepeat_TileMode);
+
+ // Draw that pattern into the target rect, setting the origin to the top
+ // left corner of the scrollbar track (so the checked rect below the thumb
+ // aligns properly with the portion above the thumb).
+ SkMatrix matrix;
+ matrix.setTranslate(SkIntToScalar(align_rect->left),
+ SkIntToScalar(align_rect->top));
+ shader->setLocalMatrix(matrix);
+ SkPaint paint;
+ paint.setShader(shader)->unref();
+ canvas->drawIRect(RECTToSkIRect(*target_rect), paint);
+ }
+ if (classic_state & DFCS_PUSHED)
+ InvertRect(hdc, target_rect);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(SCROLLBAR);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+
+ // Draw it manually.
+ if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT))
+ DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+ // Classic mode doesn't have a gripper.
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintStatusGripper(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(STATUS);
+ if (handle && draw_theme_) {
+ // Paint the status bar gripper. There doesn't seem to be a
+ // standard gripper in Windows for the space between
+ // scrollbars. This is pretty close, but it's supposed to be
+ // painted over a status bar.
+ return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, 0);
+ }
+
+ // Draw a windows classic scrollbar gripper.
+ DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(WINDOW);
+ if (handle && draw_theme_) {
+ return draw_theme_(handle, hdc, WP_DIALOG,
+ active ? FS_ACTIVE : FS_INACTIVE, rect, NULL);
+ }
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const {
+ HANDLE handle = GetThemeHandle(TAB);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL);
+
+ // Classic just renders a flat color background.
+ FillRect(hdc, rect, reinterpret_cast<HBRUSH>(COLOR_3DFACE + 1));
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintListBackground(HDC hdc,
+ bool enabled,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(LIST);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL);
+
+ // Draw it manually.
+ HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW);
+ FillRect(hdc, rect, bg_brush);
+ DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ return S_OK;
+}
+
+bool NativeTheme::IsThemingActive() const {
+ if (is_theme_active_)
+ return !!is_theme_active_();
+ return false;
+}
+
+HRESULT NativeTheme::PaintMenuArrow(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ MenuArrowDirection arrow_direction,
+ bool is_highlighted) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ if (arrow_direction == RIGHT_POINTING_ARROW) {
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ } else {
+ // There is no way to tell the uxtheme API to draw a left pointing arrow;
+ // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they
+ // are needed for RTL locales on Vista. So use a memory DC and mirror
+ // the region with GDI's StretchBlt.
+ Rect r(*rect);
+ ScopedHDC mem_dc(CreateCompatibleDC(hdc));
+ ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(),
+ r.height()));
+ HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap);
+ // Copy and horizontally mirror the background from hdc into mem_dc. Use
+ // a negative-width source rect, starting at the rightmost pixel.
+ StretchBlt(mem_dc, 0, 0, r.width(), r.height(),
+ hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY);
+ // Draw the arrow.
+ RECT theme_rect = {0, 0, r.width(), r.height()};
+ HRESULT result = draw_theme_(handle, mem_dc, part_id,
+ state_id, &theme_rect, NULL);
+ // Copy and mirror the result back into mem_dc.
+ StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(),
+ mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY);
+ SelectObject(mem_dc, old_bitmap);
+ return result;
+ }
+ }
+
+ // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a
+ // left pointing arrow. This makes the following 'if' statement slightly
+ // counterintuitive.
+ UINT state;
+ if (arrow_direction == RIGHT_POINTING_ARROW)
+ state = DFCS_MENUARROW;
+ else
+ state = DFCS_MENUARROWRIGHT;
+ return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted);
+}
+
+HRESULT NativeTheme::PaintMenuBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW));
+ return result;
+ }
+
+ FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU));
+ DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ // Nothing to do for background.
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuCheck(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ bool is_highlighted) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_) {
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ }
+ return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted);
+}
+
+HRESULT NativeTheme::PaintMenuGutter(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::PaintMenuSeparator(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP);
+ return S_OK;
+}
+
+HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ bool selected,
+ RECT* rect) const {
+ HANDLE handle = GetThemeHandle(MENU);
+ if (handle && draw_theme_)
+ return draw_theme_(handle, hdc, part_id, state_id, rect, NULL);
+ if (selected)
+ FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT));
+ return S_OK;
+}
+
+HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const {
+ HANDLE handle = GetThemeHandle(theme_name);
+ if (handle && get_theme_part_size_)
+ return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size);
+
+ return E_NOTIMPL;
+}
+
+HRESULT NativeTheme::GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_color_) {
+ COLORREF color_ref;
+ if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) ==
+ S_OK) {
+ *color = gfx::COLORREFToSkColor(color_ref);
+ return S_OK;
+ }
+ }
+ return E_NOTIMPL;
+}
+
+SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const {
+ SkColor color;
+ if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK)
+ color = gfx::COLORREFToSkColor(GetSysColor(default_sys_color));
+ return color;
+}
+
+HRESULT NativeTheme::GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value) const {
+ HANDLE handle = GetThemeHandle(theme);
+ if (handle && get_theme_int_)
+ return get_theme_int_(handle, part_id, state_id, prop_id, value);
+ return E_NOTIMPL;
+}
+
+Size NativeTheme::GetThemeBorderSize(ThemeName theme) const {
+ // For simplicity use the wildcard state==0, part==0, since it works
+ // for the cases we currently depend on.
+ int border;
+ if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK)
+ return Size(border, border);
+ else
+ return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE));
+}
+
+
+void NativeTheme::DisableTheming() const {
+ if (!set_theme_properties_)
+ return;
+ set_theme_properties_(0);
+}
+
+HRESULT NativeTheme::PaintFrameControl(HDC hdc,
+ RECT* rect,
+ UINT type,
+ UINT state,
+ bool is_highlighted) const {
+ const int width = rect->right - rect->left;
+ const int height = rect->bottom - rect->top;
+
+ // DrawFrameControl for menu arrow/check wants a monochrome bitmap.
+ ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL));
+
+ if (mask_bitmap == NULL)
+ return E_OUTOFMEMORY;
+
+ ScopedHDC bitmap_dc(CreateCompatibleDC(NULL));
+ HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap);
+ RECT local_rect = { 0, 0, width, height };
+ DrawFrameControl(bitmap_dc, &local_rect, type, state);
+
+ // We're going to use BitBlt with a b&w mask. This results in using the dest
+ // dc's text color for the black bits in the mask, and the dest dc's
+ // background color for the white bits in the mask. DrawFrameControl draws the
+ // check in black, and the background in white.
+ COLORREF old_bg_color =
+ SetBkColor(hdc,
+ GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU));
+ COLORREF old_text_color =
+ SetTextColor(hdc,
+ GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT :
+ COLOR_MENUTEXT));
+ BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY);
+ SetBkColor(hdc, old_bg_color);
+ SetTextColor(hdc, old_text_color);
+
+ SelectObject(bitmap_dc, org_bitmap);
+
+ return S_OK;
+}
+
+void NativeTheme::CloseHandles() const
+{
+ if (!close_theme_)
+ return;
+
+ for (int i = 0; i < LAST; ++i) {
+ if (theme_handles_[i])
+ close_theme_(theme_handles_[i]);
+ theme_handles_[i] = NULL;
+ }
+}
+
+HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const
+{
+ if (!open_theme_ || theme_name < 0 || theme_name >= LAST)
+ return 0;
+
+ if (theme_handles_[theme_name])
+ return theme_handles_[theme_name];
+
+ // Not found, try to load it.
+ HANDLE handle = 0;
+ switch (theme_name) {
+ case NativeTheme::BUTTON:
+ handle = open_theme_(NULL, L"Button");
+ break;
+ case NativeTheme::TEXTFIELD:
+ handle = open_theme_(NULL, L"Edit");
+ break;
+ case NativeTheme::MENULIST:
+ handle = open_theme_(NULL, L"Combobox");
+ break;
+ case NativeTheme::SCROLLBAR:
+ handle = open_theme_(NULL, L"Scrollbar");
+ break;
+ case NativeTheme::STATUS:
+ handle = open_theme_(NULL, L"Status");
+ break;
+ case NativeTheme::MENU:
+ handle = open_theme_(NULL, L"Menu");
+ break;
+ case NativeTheme::WINDOW:
+ handle = open_theme_(NULL, L"Window");
+ break;
+ case NativeTheme::TAB:
+ handle = open_theme_(NULL, L"Tab");
+ break;
+ case NativeTheme::LIST:
+ handle = open_theme_(NULL, L"Listview");
+ break;
+ default:
+ NOTREACHED();
+ }
+ theme_handles_[theme_name] = handle;
+ return handle;
+}
+
+} // namespace gfx
diff --git a/base/gfx/native_theme.h b/base/gfx/native_theme.h
new file mode 100644
index 0000000..918a282
--- /dev/null
+++ b/base/gfx/native_theme.h
@@ -0,0 +1,310 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A wrapper class for working with custom XP/Vista themes provided in
+// uxtheme.dll. This is a singleton class that can be grabbed using
+// NativeTheme::instance().
+// For more information on visual style parts and states, see:
+// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp
+
+#ifndef BASE_GFX_NATIVE_THEME_H__
+#define BASE_GFX_NATIVE_THEME_H__
+
+#include <windows.h>
+#include <uxtheme.h>
+#include "base/basictypes.h"
+#include "base/gfx/size.h"
+#include "skia/include/SkColor.h"
+
+namespace gfx {
+class PlatformCanvas;
+
+// TODO: Define class member enums to replace part_id and state_id parameters
+// that are currently defined in <vssym32.h>. Afterward, classic_state should
+// be removed and class users wouldn't need to include <vssym32.h> anymore.
+// This would enable HOT state on non-themed UI (like when RDP'ing) and would
+// simplify usage.
+// TODO: This class should probably be changed to be platform independent at
+// the same time.
+class NativeTheme {
+ public:
+ enum ThemeName {
+ BUTTON,
+ TEXTFIELD,
+ MENULIST,
+ SCROLLBAR,
+ STATUS,
+ MENU,
+ WINDOW,
+ TAB,
+ LIST,
+ LAST
+ };
+
+ // This enumeration is used within PaintMenuArrow in order to indicate the
+ // direction the menu arrow should point to.
+ enum MenuArrowDirection {
+ LEFT_POINTING_ARROW,
+ RIGHT_POINTING_ARROW
+ };
+
+ typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const RECT* clip_rect);
+ typedef HRESULT (WINAPI* DrawThemeBackgroundExPtr)(HANDLE theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ const DTBGOPTS* opts);
+ typedef HRESULT (WINAPI* GetThemeColorPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ COLORREF* color);
+ typedef HRESULT (WINAPI* GetThemeContentRectPtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ const RECT* rect,
+ RECT* content_rect);
+ typedef HRESULT (WINAPI* GetThemePartSizePtr)(HANDLE hTheme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size);
+ typedef HANDLE (WINAPI* OpenThemeDataPtr)(HWND window,
+ LPCWSTR class_list);
+ typedef HRESULT (WINAPI* CloseThemeDataPtr)(HANDLE theme);
+
+ typedef void (WINAPI* SetThemeAppPropertiesPtr) (DWORD flags);
+ typedef BOOL (WINAPI* IsThemeActivePtr)();
+ typedef HRESULT (WINAPI* GetThemeIntPtr)(HANDLE hTheme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *value);
+
+ HRESULT PaintButton(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintTextField(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect,
+ COLORREF color,
+ bool fill_content_area,
+ bool draw_edges) const;
+
+ HRESULT PaintMenuList(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ // Paints a scrollbar arrow. |classic_state| should have the appropriate
+ // classic part number ORed in already.
+ HRESULT PaintScrollbarArrow(HDC hdc,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ // Paints a scrollbar track section. |align_rect| is only used in classic
+ // mode, and makes sure the checkerboard pattern in |target_rect| is aligned
+ // with one presumed to be in |align_rect|.
+ HRESULT PaintScrollbarTrack(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* target_rect,
+ RECT* align_rect,
+ PlatformCanvas* canvas) const;
+
+ // |arrow_direction| determines whether the arrow is pointing to the left or
+ // to the right. In RTL locales, sub-menus open from right to left and
+ // therefore the menu arrow should point to the left and not to the right.
+ HRESULT PaintMenuArrow(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ MenuArrowDirection arrow_direction,
+ bool is_highlighted) const;
+
+ HRESULT PaintMenuBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuCheck(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ bool is_highlighted) const;
+
+ HRESULT PaintMenuCheckBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuGutter(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuSeparator(HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect) const;
+
+ HRESULT PaintMenuItemBackground(ThemeName theme,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ bool selected,
+ RECT* rect) const;
+
+ // Paints a scrollbar thumb or gripper.
+ HRESULT PaintScrollbarThumb(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintStatusGripper(HDC hdc,
+ int part_id,
+ int state_id,
+ int classic_state,
+ RECT* rect) const;
+
+ HRESULT PaintDialogBackground(HDC dc, bool active, RECT* rect) const;
+
+ HRESULT PaintTabPanelBackground(HDC dc, RECT* rect) const;
+
+ HRESULT PaintListBackground(HDC dc, bool enabled, RECT* rect) const;
+
+ bool IsThemingActive() const;
+
+ HRESULT GetThemePartSize(ThemeName themeName,
+ HDC hdc,
+ int part_id,
+ int state_id,
+ RECT* rect,
+ int ts,
+ SIZE* size) const;
+
+ HRESULT GetThemeColor(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ SkColor* color) const;
+
+ // Get the theme color if theming is enabled. If theming is unsupported
+ // for this part, use Win32's GetSysColor to find the color specified
+ // by default_sys_color.
+ SkColor GetThemeColorWithDefault(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int default_sys_color) const;
+
+ HRESULT GetThemeInt(ThemeName theme,
+ int part_id,
+ int state_id,
+ int prop_id,
+ int *result) const;
+
+ // Get the thickness of the border associated with the specified theme,
+ // defaulting to GetSystemMetrics edge size if themes are disabled.
+ // In Classic Windows, borders are typically 2px; on XP+, they are 1px.
+ Size GetThemeBorderSize(ThemeName theme) const;
+
+ // Disables all theming for top-level windows in the entire process, from
+ // when this method is called until the process exits. All the other
+ // methods in this class will continue to work, but their output will ignore
+ // the user's theme. This is meant for use when running tests that require
+ // consistent visual results.
+ void DisableTheming() const;
+
+ // Closes cached theme handles so we can unload the DLL or update our UI
+ // for a theme change.
+ void CloseHandles() const;
+
+ // Gets our singleton instance.
+ static const NativeTheme* instance();
+
+ private:
+ NativeTheme();
+ ~NativeTheme();
+
+ HRESULT PaintFrameControl(HDC hdc,
+ RECT* rect,
+ UINT type,
+ UINT state,
+ bool is_highlighted) const;
+
+ // Returns a handle to the theme data.
+ HANDLE GetThemeHandle(ThemeName theme_name) const;
+
+ // Function pointers into uxtheme.dll.
+ DrawThemeBackgroundPtr draw_theme_;
+ DrawThemeBackgroundExPtr draw_theme_ex_;
+ GetThemeColorPtr get_theme_color_;
+ GetThemeContentRectPtr get_theme_content_rect_;
+ GetThemePartSizePtr get_theme_part_size_;
+ OpenThemeDataPtr open_theme_;
+ CloseThemeDataPtr close_theme_;
+ SetThemeAppPropertiesPtr set_theme_properties_;
+ IsThemeActivePtr is_theme_active_;
+ GetThemeIntPtr get_theme_int_;
+
+ // Handle to uxtheme.dll.
+ HMODULE theme_dll_;
+
+ // A cache of open theme handles.
+ mutable HANDLE theme_handles_[LAST];
+
+ DISALLOW_EVIL_CONSTRUCTORS(NativeTheme);
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_NATIVE_THEME_H__
diff --git a/base/gfx/native_theme_unittest.cc b/base/gfx/native_theme_unittest.cc
new file mode 100644
index 0000000..6ed50e4
--- /dev/null
+++ b/base/gfx/native_theme_unittest.cc
@@ -0,0 +1,36 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/native_theme.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(NativeThemeTest, Init) {
+ ASSERT_TRUE(gfx::NativeTheme::instance() != NULL);
+}
diff --git a/base/gfx/platform_canvas.cc b/base/gfx/platform_canvas.cc
new file mode 100644
index 0000000..a421069
--- /dev/null
+++ b/base/gfx/platform_canvas.cc
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/platform_canvas.h"
+
+#include "base/gfx/bitmap_platform_device.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+PlatformCanvas::PlatformCanvas() : SkCanvas() {
+}
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque)
+ : SkCanvas() {
+ initialize(width, height, is_opaque, NULL);
+}
+
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section)
+ : SkCanvas() {
+ initialize(width, height, is_opaque, shared_section);
+}
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+void PlatformCanvas::initialize(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ SkDevice* device =
+ createPlatformDevice(width, height, is_opaque, shared_section);
+ setDevice(device);
+ device->unref(); // was created with refcount 1, and setDevice also refs
+}
+
+HDC PlatformCanvas::beginPlatformPaint() {
+ return getTopPlatformDevice().getBitmapDC();
+}
+
+void PlatformCanvas::endPlatformPaint() {
+ // we don't clear the DC here since it will be likely to be used again
+ // flushing will be done in onAccessBitmap
+}
+
+PlatformDevice& PlatformCanvas::getTopPlatformDevice() const {
+ // All of our devices should be our special PlatformDevice.
+ SkCanvas::LayerIter iter(const_cast<PlatformCanvas*>(this), false);
+ return *static_cast<PlatformDevice*>(iter.device());
+}
+
+SkDevice* PlatformCanvas::createDevice(SkBitmap::Config config,
+ int width,
+ int height,
+ bool is_opaque, bool isForLayer) {
+ DCHECK(config == SkBitmap::kARGB_8888_Config);
+ return createPlatformDevice(width, height, is_opaque, NULL);
+}
+
+SkDevice* PlatformCanvas::createPlatformDevice(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ HDC screen_dc = GetDC(NULL);
+ SkDevice* device = BitmapPlatformDevice::create(screen_dc, width, height,
+ is_opaque, shared_section);
+ ReleaseDC(NULL, screen_dc);
+ return device;
+}
+
+SkDevice* PlatformCanvas::setBitmapDevice(const SkBitmap&) {
+ NOTREACHED();
+ return NULL;
+}
+
+} // namespace gfx \ No newline at end of file
diff --git a/base/gfx/platform_canvas.h b/base/gfx/platform_canvas.h
new file mode 100644
index 0000000..c024a2f
--- /dev/null
+++ b/base/gfx/platform_canvas.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PLATFORM_CANVAS_H__
+#define BASE_GFX_PLATFORM_CANVAS_H__
+
+#include "base/gfx/platform_device.h"
+#include "base/basictypes.h"
+
+#include "SkCanvas.h"
+
+namespace gfx {
+
+// This class is a specialization of the regular SkCanvas that is designed to
+// work with a gfx::PlatformDevice to manage platform-specific drawing. It
+// allows using both Skia operations and platform-specific operations.
+class PlatformCanvas : public SkCanvas {
+ public:
+ // Set is_opaque if you are going to erase the bitmap and not use
+ // tranparency: this will enable some optimizations. The shared_section
+ // parameter is passed to gfx::PlatformDevice::create. See it for details.
+ //
+ // If you use the version with no arguments, you MUST call initialize()
+ PlatformCanvas();
+ PlatformCanvas(int width, int height, bool is_opaque);
+ PlatformCanvas(int width, int height, bool is_opaque, HANDLE shared_section);
+ virtual ~PlatformCanvas();
+
+ // For two-part init, call if you use the no-argument constructor above
+ void initialize(int width, int height, bool is_opaque, HANDLE shared_section);
+
+ // These calls should surround calls to platform drawing routines, the DC
+ // returned by beginPlatformPaint is the DC that can be used to draw into.
+ // Call endPlatformPaint when you are done and want to use Skia operations
+ // again; this will synchronize the bitmap to Windows.
+ virtual HDC beginPlatformPaint();
+ virtual void endPlatformPaint();
+
+ // Returns the platform device pointer of the topmost rect with a non-empty
+ // clip. In practice, this is usually either the top layer or nothing, since
+ // we usually set the clip to new layers when we make them.
+ //
+ // If there is no layer that is not all clipped out, this will return a
+ // dummy device so callers do not have to check. If you are concerned about
+ // performance, check the clip before doing any painting.
+ //
+ // This is different than SkCanvas' getDevice, because that returns the
+ // bottommost device.
+ //
+ // Danger: the resulting device should not be saved. It will be invalidated
+ // by the next call to save() or restore().
+ PlatformDevice& getTopPlatformDevice() const;
+
+ protected:
+ // Creates a device store for use by the canvas. We override this so that
+ // the device is always our own so we know that we can use GDI operations
+ // on it. Simply calls into createPlatformDevice().
+ virtual SkDevice* createDevice(SkBitmap::Config, int width, int height,
+ bool is_opaque, bool isForLayer);
+
+ // Creates a device store for use by the canvas. By default, it creates a
+ // BitmapPlatformDevice object. Can be overridden to change the object type.
+ virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque,
+ HANDLE shared_section);
+
+ private:
+ // Unimplemented.
+ virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+
+ DISALLOW_EVIL_CONSTRUCTORS(PlatformCanvas);
+};
+
+// A class designed to help with WM_PAINT operations on Windows. It will
+// do BeginPaint/EndPaint on init/destruction, and will create the bitmap and
+// canvas with the correct size and transform for the dirty rect. The bitmap
+// will be automatically painted to the screen on destruction.
+//
+// You MUST call isEmpty before painting to determine if anything needs
+// painting. Sometimes the dirty rect can actually be empty, and this makes
+// the bitmap functions we call unhappy. The caller should not paint in this
+// case.
+//
+// Therefore, all you need to do is:
+// case WM_PAINT: {
+// gfx::PlatformCanvasPaint canvas(hwnd);
+// if (!canvas.isEmpty()) {
+// ... paint to the canvas ...
+// }
+// return 0;
+// }
+template <class T>
+class CanvasPaintT : public T {
+ public:
+ CanvasPaintT(HWND hwnd) : hwnd_(hwnd), for_paint_(true) {
+ initPaint(true);
+ }
+
+ CanvasPaintT(HWND hwnd, bool opaque) : hwnd_(hwnd), for_paint_(true) {
+ initPaint(opaque);
+ }
+
+ // Creates a CanvasPaintT for the specified region that paints to the
+ // specified dc. This does NOT do BeginPaint/EndPaint.
+ CanvasPaintT(HDC dc, bool opaque, int x, int y, int w, int h)
+ : hwnd_(NULL),
+ paint_dc_(dc),
+ for_paint_(false) {
+ memset(&ps_, 0, sizeof(ps_));
+ ps_.rcPaint.left = x;
+ ps_.rcPaint.right = x + w;
+ ps_.rcPaint.top = y;
+ ps_.rcPaint.bottom = y + h;
+ init(opaque);
+ }
+
+
+ virtual ~CanvasPaintT() {
+ if (!isEmpty()) {
+ restoreToCount(1);
+ // Commit the drawing to the screen
+ getTopPlatformDevice().drawToHDC(paint_dc_,
+ ps_.rcPaint.left, ps_.rcPaint.top,
+ NULL);
+ }
+ if (for_paint_)
+ EndPaint(hwnd_, &ps_);
+ }
+
+ // Returns true if the invalid region is empty. The caller should call this
+ // function to determine if anything needs painting.
+ bool isEmpty() const {
+ return ps_.rcPaint.right - ps_.rcPaint.left == 0 ||
+ ps_.rcPaint.bottom - ps_.rcPaint.top == 0;
+ }
+
+ // Use to access the Windows painting parameters, especially useful for
+ // getting the bounding rect for painting: paintstruct().rcPaint
+ const PAINTSTRUCT& paintStruct() const {
+ return ps_;
+ }
+
+ // Returns the DC that will be painted to
+ HDC paintDC() const {
+ return paint_dc_;
+ }
+
+ protected:
+ HWND hwnd_;
+ HDC paint_dc_;
+ PAINTSTRUCT ps_;
+
+ private:
+ void initPaint(bool opaque) {
+ paint_dc_ = BeginPaint(hwnd_, &ps_);
+
+ init(opaque);
+ }
+
+ void init(bool opaque) {
+ // FIXME(brettw) for ClearType, we probably want to expand the bounds of
+ // painting by one pixel so that the boundaries will be correct (ClearType
+ // text can depend on the adjacent pixel). Then we would paint just the inset
+ // pixels to the screen.
+ initialize(ps_.rcPaint.right - ps_.rcPaint.left,
+ ps_.rcPaint.bottom - ps_.rcPaint.top, opaque, NULL);
+
+ // This will bring the canvas into the screen coordinate system for the
+ // dirty rect
+ translate(SkIntToScalar(-ps_.rcPaint.left),
+ SkIntToScalar(-ps_.rcPaint.top));
+ }
+
+ // If true, this canvas was created for a BeginPaint.
+ const bool for_paint_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CanvasPaintT);
+};
+
+typedef CanvasPaintT<PlatformCanvas> PlatformCanvasPaint;
+
+} // namespace gfx
+
+#endif // BASE_GFX_PLATFORM_CANVAS_H__
diff --git a/base/gfx/platform_canvas_unittest.cc b/base/gfx/platform_canvas_unittest.cc
new file mode 100644
index 0000000..2d127a7
--- /dev/null
+++ b/base/gfx/platform_canvas_unittest.cc
@@ -0,0 +1,293 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/platform_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "SkColor.h"
+
+namespace gfx {
+
+namespace {
+
+// Return true if the canvas is filled to canvas_color,
+// and contains a single rectangle filled to rect_color.
+bool VerifyRect(const PlatformCanvas& canvas,
+ uint32_t canvas_color, uint32_t rect_color,
+ int x, int y, int w, int h) {
+ PlatformDevice& device = canvas.getTopPlatformDevice();
+ const SkBitmap& bitmap = device.accessBitmap(false);
+ SkAutoLockPixels lock(bitmap);
+
+ for (int cur_y = 0; cur_y < bitmap.height(); cur_y++) {
+ for (int cur_x = 0; cur_x < bitmap.width(); cur_x++) {
+ if (cur_x >= x && cur_x < x + w &&
+ cur_y >= y && cur_y < y + h) {
+ // Inside the square should be rect_color
+ if (*bitmap.getAddr32(cur_x, cur_y) != rect_color)
+ return false;
+ } else {
+ // Outside the square should be canvas_color
+ if (*bitmap.getAddr32(cur_x, cur_y) != canvas_color)
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+// Checks whether there is a white canvas with a black square at the given
+// location in pixels (not in the canvas coordinate system).
+// TODO(ericroman): rename Square to Rect
+bool VerifyBlackSquare(const PlatformCanvas& canvas, int x, int y, int w, int h) {
+ return VerifyRect(canvas, SK_ColorWHITE, SK_ColorBLACK, x, y, w, h);
+}
+
+// Check that every pixel in the canvas is a single color.
+bool VerifyCanvasColor(const PlatformCanvas& canvas, uint32_t canvas_color) {
+ return VerifyRect(canvas, canvas_color, 0, 0, 0, 0, 0);
+}
+
+void DrawGDIRect(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ HDC dc = canvas.beginPlatformPaint();
+
+ RECT inner_rc;
+ inner_rc.left = x;
+ inner_rc.top = y;
+ inner_rc.right = x + w;
+ inner_rc.bottom = y + h;
+ FillRect(dc, &inner_rc, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
+
+ canvas.endPlatformPaint();
+}
+
+// Clips the contents of the canvas to the given rectangle. This will be
+// intersected with any existing clip.
+void AddClip(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ SkRect rect;
+ rect.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + w), SkIntToScalar(y + h));
+ canvas.clipRect(rect);
+}
+
+class LayerSaver {
+ public:
+ LayerSaver(PlatformCanvas& canvas, int x, int y, int w, int h)
+ : canvas_(canvas),
+ x_(x),
+ y_(y),
+ w_(w),
+ h_(h) {
+ SkRect bounds;
+ bounds.set(SkIntToScalar(x_), SkIntToScalar(y_),
+ SkIntToScalar(right()), SkIntToScalar(bottom()));
+ canvas_.saveLayer(&bounds, NULL);
+ }
+
+ ~LayerSaver() {
+ canvas_.getTopPlatformDevice().fixupAlphaBeforeCompositing();
+ canvas_.restore();
+ }
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+ int w() const { return w_; }
+ int h() const { return h_; }
+
+ // Returns the EXCLUSIVE far bounds of the layer.
+ int right() const { return x_ + w_; }
+ int bottom() const { return y_ + h_; }
+
+ private:
+ PlatformCanvas& canvas_;
+ int x_, y_, w_, h_;
+};
+
+// Size used for making layers in many of the below tests.
+const int kLayerX = 2;
+const int kLayerY = 3;
+const int kLayerW = 9;
+const int kLayerH = 7;
+
+// Size used by some tests to draw a rectangle inside the layer.
+const int kInnerX = 4;
+const int kInnerY = 5;
+const int kInnerW = 2;
+const int kInnerH = 3;
+
+}
+
+// This just checks that our checking code is working properly, it just uses
+// regular skia primitives.
+TEST(PlatformCanvas, SkLayer) {
+ // Create the canvas initialized to opaque white.
+ PlatformCanvas canvas(16, 16, true);
+ canvas.drawColor(SK_ColorWHITE);
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas.drawColor(SK_ColorBLACK);
+ }
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+}
+
+// Test the GDI clipping.
+TEST(PlatformCanvas, GDIClipRegion) {
+ // Initialize a white canvas
+ PlatformCanvas canvas(16, 16, true);
+ canvas.drawColor(SK_ColorWHITE);
+ EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+
+ // Test that initially the canvas has no clip region, by filling it
+ // with a black rectangle.
+ // Note: Don't use LayerSaver, since internally it sets a clip region.
+ DrawGDIRect(canvas, 0, 0, 16, 16);
+ canvas.getTopPlatformDevice().fixupAlphaBeforeCompositing();
+ EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorBLACK));
+
+ // Test that intersecting disjoint clip rectangles sets an empty clip region
+ canvas.drawColor(SK_ColorWHITE);
+ EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+ {
+ LayerSaver layer(canvas, 0, 0, 16, 16);
+ AddClip(canvas, 2, 3, 4, 5);
+ AddClip(canvas, 4, 9, 10, 10);
+ DrawGDIRect(canvas, 0, 0, 16, 16);
+ }
+ EXPECT_TRUE(VerifyCanvasColor(canvas, SK_ColorWHITE));
+}
+
+// Test the layers get filled properly by GDI.
+TEST(PlatformCanvas, GDILayer) {
+ // Create the canvas initialized to opaque white.
+ PlatformCanvas canvas(16, 16, true);
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ canvas.drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawGDIRect(canvas, 0, 0, 100, 100);
+ }
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+
+ // Make a layer and fill it partially to make sure the translation is correct.
+ canvas.drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ }
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+ // Add a clip on the layer and fill to make make sure clip is correct.
+ canvas.drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas.save();
+ AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ DrawGDIRect(canvas, 0, 0, 100, 100);
+ canvas.restore();
+ }
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+ // Add a clip and then make the layer to make sure the clip is correct.
+ canvas.drawColor(SK_ColorWHITE);
+ canvas.save();
+ AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawGDIRect(canvas, 0, 0, 100, 100);
+ }
+ canvas.restore();
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+}
+
+// Test that translation + make layer works properly.
+TEST(PlatformCanvas, GDITranslateLayer) {
+ // Create the canvas initialized to opaque white.
+ PlatformCanvas canvas(16, 16, true);
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ canvas.drawColor(SK_ColorWHITE);
+ canvas.save();
+ canvas.translate(1, 1);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawGDIRect(canvas, 0, 0, 100, 100);
+ }
+ canvas.restore();
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kLayerX + 1, kLayerY + 1,
+ kLayerW, kLayerH));
+
+ // Translate then make the layer.
+ canvas.drawColor(SK_ColorWHITE);
+ canvas.save();
+ canvas.translate(1, 1);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ }
+ canvas.restore();
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 1, kInnerY + 1,
+ kInnerW, kInnerH));
+
+ // Make the layer then translate.
+ canvas.drawColor(SK_ColorWHITE);
+ canvas.save();
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas.translate(1, 1);
+ DrawGDIRect(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ }
+ canvas.restore();
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 1, kInnerY + 1,
+ kInnerW, kInnerH));
+
+ // Translate both before and after, and have a clip.
+ canvas.drawColor(SK_ColorWHITE);
+ canvas.save();
+ canvas.translate(1, 1);
+ {
+ LayerSaver layer(canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas.translate(1, 1);
+ AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ DrawGDIRect(canvas, 0, 0, 100, 100);
+ }
+ canvas.restore();
+ EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 2, kInnerY + 2,
+ kInnerW, kInnerH));
+}
+
+} // namespace
diff --git a/base/gfx/platform_device.cc b/base/gfx/platform_device.cc
new file mode 100644
index 0000000..e23ccf3
--- /dev/null
+++ b/base/gfx/platform_device.cc
@@ -0,0 +1,253 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/platform_device.h"
+
+#include "base/logging.h"
+#include "base/gfx/skia_utils.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkUtils.h"
+
+namespace gfx {
+
+PlatformDevice::PlatformDevice(const SkBitmap& bitmap)
+ : SkDevice(bitmap) {
+}
+
+// static
+void PlatformDevice::InitializeDC(HDC context) {
+ // Enables world transformation.
+ // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the
+ // counterclockwise direction in logical space. This is equivalent to the
+ // statement that, in the GM_ADVANCED graphics mode, both arc control points
+ // and arcs themselves fully respect the device context's world-to-device
+ // transformation.
+ BOOL res = SetGraphicsMode(context, GM_ADVANCED);
+ DCHECK_NE(res, 0);
+
+ // Enables dithering.
+ res = SetStretchBltMode(context, HALFTONE);
+ DCHECK_NE(res, 0);
+ // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called
+ // right after.
+ res = SetBrushOrgEx(context, 0, 0, NULL);
+ DCHECK_NE(res, 0);
+
+ // Sets up default orientation.
+ res = SetArcDirection(context, AD_CLOCKWISE);
+ DCHECK_NE(res, 0);
+
+ // Sets up default colors.
+ res = SetBkColor(context, RGB(255, 255, 255));
+ DCHECK_NE(res, CLR_INVALID);
+ res = SetTextColor(context, RGB(0, 0, 0));
+ DCHECK_NE(res, CLR_INVALID);
+ res = SetDCBrushColor(context, RGB(255, 255, 255));
+ DCHECK_NE(res, CLR_INVALID);
+ res = SetDCPenColor(context, RGB(0, 0, 0));
+ DCHECK_NE(res, CLR_INVALID);
+
+ // Sets up default transparency.
+ res = SetBkMode(context, OPAQUE);
+ DCHECK_NE(res, 0);
+ res = SetROP2(context, R2_COPYPEN);
+ DCHECK_NE(res, 0);
+}
+
+// static
+void PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) {
+ switch (path.getFillType()) {
+ case SkPath::kWinding_FillType: {
+ int res = SetPolyFillMode(context, WINDING);
+ DCHECK(res != 0);
+ break;
+ }
+ case SkPath::kEvenOdd_FillType: {
+ int res = SetPolyFillMode(context, ALTERNATE);
+ DCHECK(res != 0);
+ break;
+ }
+ default: {
+ NOTREACHED();
+ break;
+ }
+ }
+ BOOL res = BeginPath(context);
+ DCHECK(res != 0);
+
+ CubicPaths paths;
+ if (!SkPathToCubicPaths(&paths, path))
+ return;
+
+ std::vector<POINT> points;
+ for (CubicPaths::const_iterator path(paths.begin()); path != paths.end();
+ ++path) {
+ if (!path->size())
+ continue;
+ // DCHECK_EQ(points.size() % 4, 0);
+ points.resize(0);
+ points.reserve(path->size() * 3 / 4 + 1);
+ points.push_back(SkPointToPOINT(path->front().p[0]));
+ for (CubicPath::const_iterator point(path->begin()); point != path->end();
+ ++point) {
+ // Never add point->p[0]
+ points.push_back(SkPointToPOINT(point->p[1]));
+ points.push_back(SkPointToPOINT(point->p[2]));
+ points.push_back(SkPointToPOINT(point->p[3]));
+ }
+ DCHECK_EQ((points.size() - 1) % 3, 0);
+ // This is slightly inefficient since all straight line and quadratic lines
+ // are "upgraded" to a cubic line.
+ // TODO(maruel): http://b/1147346 We should use
+ // PolyDraw/PolyBezier/Polyline whenever possible.
+ res = PolyBezier(context, &points.front(),
+ static_cast<DWORD>(points.size()));
+ DCHECK_NE(res, 0);
+ if (res == 0)
+ break;
+ }
+ if (res == 0) {
+ // Make sure the path is discarded.
+ AbortPath(context);
+ } else {
+ res = EndPath(context);
+ DCHECK(res != 0);
+ }
+}
+
+// static
+void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
+ XFORM xf;
+ xf.eM11 = matrix[SkMatrix::kMScaleX];
+ xf.eM21 = matrix[SkMatrix::kMSkewX];
+ xf.eDx = matrix[SkMatrix::kMTransX];
+ xf.eM12 = matrix[SkMatrix::kMSkewY];
+ xf.eM22 = matrix[SkMatrix::kMScaleY];
+ xf.eDy = matrix[SkMatrix::kMTransY];
+ SetWorldTransform(dc, &xf);
+}
+
+// static
+bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths,
+ const SkPath& skpath) {
+ paths->clear();
+ CubicPath* current_path = NULL;
+ SkPoint current_points[4];
+ CubicPoints points_to_add;
+ SkPath::Iter iter(skpath, false);
+ for (SkPath::Verb verb = iter.next(current_points);
+ verb != SkPath::kDone_Verb;
+ verb = iter.next(current_points)) {
+ switch (verb) {
+ case SkPath::kMove_Verb: { // iter.next returns 1 point
+ // Ignores it since the point is copied in the next operation. See
+ // SkPath::Iter::next() for reference.
+ paths->push_back(CubicPath());
+ current_path = &paths->back();
+ // Skip point addition.
+ continue;
+ }
+ case SkPath::kLine_Verb: { // iter.next returns 2 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[0];
+ points_to_add.p[2] = current_points[1];
+ points_to_add.p[3] = current_points[1];
+ break;
+ }
+ case SkPath::kQuad_Verb: { // iter.next returns 3 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[1];
+ points_to_add.p[2] = current_points[2];
+ points_to_add.p[3] = current_points[2];
+ break;
+ }
+ case SkPath::kCubic_Verb: { // iter.next returns 4 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[1];
+ points_to_add.p[2] = current_points[2];
+ points_to_add.p[3] = current_points[3];
+ break;
+ }
+ case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point)
+ paths->push_back(CubicPath());
+ current_path = &paths->back();
+ continue;
+ }
+ case SkPath::kDone_Verb: // iter.next returns 0 points
+ default: {
+ current_path = NULL;
+ // Will return false.
+ break;
+ }
+ }
+ DCHECK(current_path);
+ if (!current_path) {
+ paths->clear();
+ return false;
+ }
+ current_path->push_back(points_to_add);
+ }
+ return true;
+}
+
+// static
+void PlatformDevice::LoadClippingRegionToDC(HDC context,
+ const SkRegion& region,
+ const SkMatrix& transformation) {
+ HRGN hrgn;
+ if (region.isEmpty()) {
+ // region can be empty, in which case everything will be clipped.
+ hrgn = CreateRectRgn(0, 0, 0, 0);
+ } else if (region.isRect()) {
+ // Do the transformation.
+ SkRect rect;
+ rect.set(region.getBounds());
+ transformation.mapRect(&rect);
+ SkIRect irect;
+ rect.round(&irect);
+ hrgn = CreateRectRgnIndirect(&SkIRectToRECT(irect));
+ } else {
+ // It is complex.
+ SkPath path;
+ region.getBoundaryPath(&path);
+ // Clip. Note that windows clipping regions are not affected by the
+ // transform so apply it manually.
+ path.transform(transformation);
+ LoadPathToDC(context, path);
+ hrgn = PathToRegion(context);
+ }
+ int result = SelectClipRgn(context, hrgn);
+ DCHECK_NE(result, ERROR);
+ result = DeleteObject(hrgn);
+ DCHECK_NE(result, 0);
+}
+
+} // namespace gfx
diff --git a/base/gfx/platform_device.h b/base/gfx/platform_device.h
new file mode 100644
index 0000000..c3dc998
--- /dev/null
+++ b/base/gfx/platform_device.h
@@ -0,0 +1,120 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PLATFORM_DEVICE_H__
+#define BASE_GFX_PLATFORM_DEVICE_H__
+
+#include <vector>
+
+#include "SkDevice.h"
+
+class SkMatrix;
+class SkPath;
+class SkRegion;
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface Windows can also write
+// to. It also provides functionality to play well with GDI drawing functions.
+// This class is abstract and must be subclassed. It provides the basic
+// interface to implement it either with or without a bitmap backend.
+class PlatformDevice : public SkDevice {
+ public:
+ // The DC that corresponds to the bitmap, used for GDI operations drawing
+ // into the bitmap. This is possibly heavyweight, so it should be existant
+ // only during one pass of rendering.
+ virtual HDC getBitmapDC() = 0;
+
+ // Draws to the given screen DC, if the bitmap DC doesn't exist, this will
+ // temporarily create it. However, if you have created the bitmap DC, it will
+ // be more efficient if you don't free it until after this call so it doesn't
+ // have to be created twice. If src_rect is null, then the entirety of the
+ // source device will be copied.
+ virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect) = 0;
+
+ // Invoke before using GDI functions. See description in platform_device.cc
+ // for specifics.
+ // NOTE: x,y,width and height are relative to the current transform.
+ virtual void prepareForGDI(int x, int y, int width, int height) { }
+
+ // Invoke after using GDI functions. See description in platform_device.cc
+ // for specifics.
+ // NOTE: x,y,width and height are relative to the current transform.
+ virtual void postProcessGDI(int x, int y, int width, int height) { }
+
+ // Sets the opacity of each pixel in the specified region to be opaque.
+ virtual void makeOpaque(int x, int y, int width, int height) { }
+
+ // Call this function to fix the alpha channels before compositing this layer
+ // onto another. Internally, the device uses a special alpha method to work
+ // around problems with Windows. This call will put the values into what
+ // Skia expects, so it can be composited onto other layers.
+ //
+ // After this call, no more drawing can be done because the
+ // alpha channels will be "correct", which, if this function is called again
+ // will make them wrong. See the implementation for more discussion.
+ virtual void fixupAlphaBeforeCompositing() { }
+
+ // Returns if the preferred rendering engine is vectorial or bitmap based.
+ virtual bool IsVectorial() = 0;
+
+ // Initializes the default settings and colors in a device context.
+ static void InitializeDC(HDC context);
+
+ // Loads a SkPath into the GDI context. The path can there after be used for
+ // clipping or as a stroke.
+ static void LoadPathToDC(HDC context, const SkPath& path);
+
+ // Loads a SkRegion into the GDI context.
+ static void LoadClippingRegionToDC(HDC context, const SkRegion& region,
+ const SkMatrix& transformation);
+
+ protected:
+ // Arrays must be inside structures.
+ struct CubicPoints {
+ SkPoint p[4];
+ };
+ typedef std::vector<CubicPoints> CubicPath;
+ typedef std::vector<CubicPath> CubicPaths;
+
+ // Forwards |bitmap| to SkDevice's constructor.
+ PlatformDevice(const SkBitmap& bitmap);
+
+ // Loads the specified Skia transform into the device context, excluding
+ // perspective (which GDI doesn't support).
+ static void LoadTransformToDC(HDC dc, const SkMatrix& matrix);
+
+ // Transforms SkPath's paths into a series of cubic path.
+ static bool SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath);
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_PLATFORM_DEVICE_H__
diff --git a/base/gfx/png_codec_unittest.cc b/base/gfx/png_codec_unittest.cc
new file mode 100644
index 0000000..1b510aa
--- /dev/null
+++ b/base/gfx/png_codec_unittest.cc
@@ -0,0 +1,223 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <math.h>
+
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/png_decoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+static void MakeRGBImage(int w, int h, std::vector<unsigned char>* dat) {
+ dat->resize(w * h * 3);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &(*dat)[(y * w + x) * 3];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ }
+ }
+}
+
+// Set use_transparency to write data into the alpha channel, otherwise it will
+// be filled with 0xff. With the alpha channel stripped, this should yield the
+// same image as MakeRGBImage above, so the code below can make reference
+// images for conversion testing.
+static void MakeRGBAImage(int w, int h, bool use_transparency,
+ std::vector<unsigned char>* dat) {
+ dat->resize(w * h * 4);
+ for (int y = 0; y < h; y++) {
+ for (int x = 0; x < w; x++) {
+ unsigned char* org_px = &(*dat)[(y * w + x) * 4];
+ org_px[0] = x * 3; // r
+ org_px[1] = x * 3 + 1; // g
+ org_px[2] = x * 3 + 2; // b
+ if (use_transparency)
+ org_px[3] = x*3 + 3; // a
+ else
+ org_px[3] = 0xFF; // a (opaque)
+ }
+ }
+}
+
+TEST(PNGCodec, EncodeDecodeRGB) {
+ const int w = 20, h = 20;
+
+ // create an image with known values
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h,
+ w * 3, false, &encoded));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+ PNGDecoder::FORMAT_RGB, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be equal
+ ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, EncodeDecodeRGBA) {
+ const int w = 20, h = 20;
+
+ // create an image with known values, a must be opaque because it will be
+ // lost during encoding
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, true, &original);
+
+ // encode
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGBA, w, h,
+ w * 4, false, &encoded));
+
+ // decode, it should have the same size as the original
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+ PNGDecoder::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be exactly equal
+ ASSERT_TRUE(original == decoded);
+}
+
+// Test that corrupted data decompression causes failures.
+TEST(PNGCodec, DecodeCorrupted) {
+ int w = 20, h = 20;
+
+ // Make some random data (an uncompressed image).
+ std::vector<unsigned char> original;
+ MakeRGBImage(w, h, &original);
+
+ // It should fail when given non-JPEG compressed data.
+ std::vector<unsigned char> output;
+ int outw, outh;
+ EXPECT_FALSE(PNGDecoder::Decode(&original[0], original.size(),
+ PNGDecoder::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // Make some compressed data.
+ std::vector<unsigned char> compressed;
+ EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h,
+ w * 3, false, &compressed));
+
+ // Try decompressing a truncated version.
+ EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size() / 2,
+ PNGDecoder::FORMAT_RGB, &output,
+ &outw, &outh));
+
+ // Corrupt it and try decompressing that.
+ for (int i = 10; i < 30; i++)
+ compressed[i] = i;
+ EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size(),
+ PNGDecoder::FORMAT_RGB, &output,
+ &outw, &outh));
+}
+
+TEST(PNGCodec, EncodeDecodeBGRA) {
+ const int w = 20, h = 20;
+
+ // Create an image with known values, alpha must be opaque because it will be
+ // lost during encoding.
+ std::vector<unsigned char> original;
+ MakeRGBAImage(w, h, true, &original);
+
+ // Encode.
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_BGRA, w, h,
+ w * 4, false, &encoded));
+
+ // Decode, it should have the same size as the original.
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+ PNGDecoder::FORMAT_BGRA, &decoded,
+ &outw, &outh));
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original.size(), decoded.size());
+
+ // Images must be exactly equal.
+ ASSERT_TRUE(original == decoded);
+}
+
+TEST(PNGCodec, StripAddAlpha) {
+ const int w = 20, h = 20;
+
+ // These should be the same except one has a 0xff alpha channel.
+ std::vector<unsigned char> original_rgb;
+ MakeRGBImage(w, h, &original_rgb);
+ std::vector<unsigned char> original_rgba;
+ MakeRGBAImage(w, h, false, &original_rgba);
+
+ // Encode RGBA data as RGB.
+ std::vector<unsigned char> encoded;
+ EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], PNGEncoder::FORMAT_RGBA, w, h,
+ w * 4, true, &encoded));
+
+ // Decode the RGB to RGBA.
+ std::vector<unsigned char> decoded;
+ int outw, outh;
+ EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+ PNGDecoder::FORMAT_RGBA, &decoded,
+ &outw, &outh));
+
+ // Decoded and reference should be the same (opaque alpha).
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original_rgba.size(), decoded.size());
+ ASSERT_TRUE(original_rgba == decoded);
+
+ // Encode RGBA to RGBA.
+ EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], PNGEncoder::FORMAT_RGBA, w, h,
+ w * 4, false, &encoded));
+
+ // Decode the RGBA to RGB.
+ EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(),
+ PNGDecoder::FORMAT_RGB, &decoded,
+ &outw, &outh));
+
+ // It should be the same as our non-alpha-channel reference.
+ ASSERT_EQ(w, outw);
+ ASSERT_EQ(h, outh);
+ ASSERT_EQ(original_rgb.size(), decoded.size());
+ ASSERT_TRUE(original_rgb == decoded);
+}
diff --git a/base/gfx/png_decoder.cc b/base/gfx/png_decoder.cc
new file mode 100644
index 0000000..40ba2e8
--- /dev/null
+++ b/base/gfx/png_decoder.cc
@@ -0,0 +1,376 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/png_decoder.h"
+
+#include "base/logging.h"
+#include "skia/include/SkBitmap.h"
+
+extern "C" {
+#include "png.h"
+}
+
+namespace {
+
+// Converts BGRA->RGBA and RGBA->BGRA.
+void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
+ unsigned char* output) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &input[x * 4];
+ unsigned char* pixel_out = &output[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = pixel_in[3];
+ }
+}
+
+void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
+ unsigned char* rgb) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgba[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ }
+}
+
+} // namespace
+
+// Decoder --------------------------------------------------------------------
+//
+// This code is based on WebKit libpng interface (PNGImageDecoder), which is
+// in turn based on the Mozilla png decoder.
+
+namespace {
+
+// Gamma constants: We assume we're on Windows which uses a gamma of 2.2.
+const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library.
+const double kDefaultGamma = 2.2;
+const double kInverseGamma = 1.0 / kDefaultGamma;
+
+// Maximum pixel dimension we'll try to decode.
+const png_uint_32 kMaxSize = 4096;
+
+class PngDecoderState {
+ public:
+ PngDecoderState(PNGDecoder::ColorFormat ofmt, std::vector<unsigned char>* o)
+ : output_format(ofmt),
+ output_channels(0),
+ output(o),
+ row_converter(NULL),
+ width(0),
+ height(0),
+ done(false) {
+ }
+
+ PNGDecoder::ColorFormat output_format;
+ int output_channels;
+
+ std::vector<unsigned char>* output;
+
+ // Called to convert a row from the library to the correct output format.
+ // When NULL, no conversion is necessary.
+ void (*row_converter)(const unsigned char* in, int w, unsigned char* out);
+
+ // Size of the image, set in the info callback.
+ int width;
+ int height;
+
+ // Set to true when we've found the end of the data.
+ bool done;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(PngDecoderState);
+};
+
+void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width,
+ unsigned char* rgba) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ unsigned char* pixel_out = &rgba[x * 4];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ pixel_out[3] = 0xff;
+ }
+}
+
+void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width,
+ unsigned char* bgra) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgb[x * 3];
+ unsigned char* pixel_out = &bgra[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = 0xff;
+ }
+}
+
+// Called when the png header has been read. This code is based on the WebKit
+// PNGImageDecoder
+void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ int bit_depth, color_type, interlace_type, compression_type;
+ int filter_type, channels;
+ png_uint_32 w, h;
+ png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type,
+ &interlace_type, &compression_type, &filter_type);
+
+ // Bounds check. When the image is unreasonably big, we'll error out and
+ // end up back at the setjmp call when we set up decoding.
+ if (w > kMaxSize || h > kMaxSize)
+ longjmp(png_ptr->jmpbuf, 1);
+ state->width = static_cast<int>(w);
+ state->height = static_cast<int>(h);
+
+ // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA.
+ if (color_type == PNG_COLOR_TYPE_PALETTE ||
+ (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8))
+ png_set_expand(png_ptr);
+
+ // Transparency for paletted images.
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_expand(png_ptr);
+
+ // Convert 16-bit to 8-bit.
+ if (bit_depth == 16)
+ png_set_strip_16(png_ptr);
+
+ // Expand grayscale to RGB.
+ if (color_type == PNG_COLOR_TYPE_GRAY ||
+ color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+ png_set_gray_to_rgb(png_ptr);
+
+ // Deal with gamma and keep it under our control.
+ double gamma;
+ if (png_get_gAMA(png_ptr, info_ptr, &gamma)) {
+ if (gamma <= 0.0 || gamma > kMaxGamma) {
+ gamma = kInverseGamma;
+ png_set_gAMA(png_ptr, info_ptr, gamma);
+ }
+ png_set_gamma(png_ptr, kDefaultGamma, gamma);
+ } else {
+ png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma);
+ }
+
+ // Tell libpng to send us rows for interlaced pngs.
+ if (interlace_type == PNG_INTERLACE_ADAM7)
+ png_set_interlace_handling(png_ptr);
+
+ // Update our info now
+ png_read_update_info(png_ptr, info_ptr);
+ channels = png_get_channels(png_ptr, info_ptr);
+
+ // Pick our row format converter necessary for this data.
+ if (channels == 3) {
+ switch (state->output_format) {
+ case PNGDecoder::FORMAT_RGB:
+ state->row_converter = NULL; // no conversion necessary
+ state->output_channels = 3;
+ break;
+ case PNGDecoder::FORMAT_RGBA:
+ state->row_converter = &ConvertRGBtoRGBA;
+ state->output_channels = 4;
+ break;
+ case PNGDecoder::FORMAT_BGRA:
+ state->row_converter = &ConvertRGBtoBGRA;
+ state->output_channels = 4;
+ break;
+ default:
+ NOTREACHED() << "Unknown output format";
+ break;
+ }
+ } else if (channels == 4) {
+ switch (state->output_format) {
+ case PNGDecoder::FORMAT_RGB:
+ state->row_converter = &ConvertRGBAtoRGB;
+ state->output_channels = 3;
+ break;
+ case PNGDecoder::FORMAT_RGBA:
+ state->row_converter = NULL; // no conversion necessary
+ state->output_channels = 4;
+ break;
+ case PNGDecoder::FORMAT_BGRA:
+ state->row_converter = &ConvertBetweenBGRAandRGBA;
+ state->output_channels = 4;
+ break;
+ default:
+ NOTREACHED() << "Unknown output format";
+ break;
+ }
+ } else {
+ NOTREACHED() << "Unknown input channels";
+ longjmp(png_ptr->jmpbuf, 1);
+ }
+
+ state->output->resize(state->width * state->output_channels * state->height);
+}
+
+void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row,
+ png_uint_32 row_num, int pass) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is "
+ "giving us interlaced data.";
+ if (static_cast<int>(row_num) > state->height) {
+ NOTREACHED() << "Invalid row";
+ return;
+ }
+
+ unsigned char* dest = &(*state->output)[
+ state->width * state->output_channels * row_num];
+ if (state->row_converter)
+ state->row_converter(new_row, state->width, dest);
+ else
+ memcpy(dest, new_row, state->width * state->output_channels);
+}
+
+void DecodeEndCallback(png_struct* png_ptr, png_info* info) {
+ PngDecoderState* state = static_cast<PngDecoderState*>(
+ png_get_progressive_ptr(png_ptr));
+
+ // Mark the image as complete, this will tell the Decode function that we
+ // have successfully found the end of the data.
+ state->done = true;
+}
+
+// Automatically destroys the given read structs on destruction to make
+// cleanup and error handling code cleaner.
+class PngReadStructDestroyer {
+ public:
+ PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
+ }
+ ~PngReadStructDestroyer() {
+ png_destroy_read_struct(ps_, pi_, NULL);
+ }
+ private:
+ png_struct** ps_;
+ png_info** pi_;
+};
+
+} // namespace
+
+// static
+bool PNGDecoder::Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h) {
+ if (input_size < 8)
+ return false; // Input data too small to be a png
+
+ // Have libpng check the signature, it likes the first 8 bytes.
+ if (png_sig_cmp(const_cast<unsigned char*>(input), 0, 8) != 0)
+ return false;
+
+ png_struct* png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+ png_voidp_NULL,
+ png_error_ptr_NULL,
+ png_error_ptr_NULL);
+ if (!png_ptr)
+ return false;
+
+ png_info* info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ return false;
+ }
+
+ PngReadStructDestroyer destroyer(&png_ptr, &info_ptr);
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ // The destroyer will ensure that the structures are cleaned up in this
+ // case, even though we may get here as a jump from random parts of the
+ // PNG library called below.
+ return false;
+ }
+
+ PngDecoderState state(format, output);
+
+ png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback,
+ &DecodeRowCallback, &DecodeEndCallback);
+ png_process_data(png_ptr, info_ptr, const_cast<unsigned char*>(input), input_size);
+
+ if (!state.done) {
+ // Fed it all the data but the library didn't think we got all the data, so
+ // this file must be truncated.
+ output->clear();
+ return false;
+ }
+
+ *w = state.width;
+ *h = state.height;
+ return true;
+}
+
+// static
+bool PNGDecoder::Decode(const std::vector<unsigned char>* data,
+ SkBitmap* bitmap) {
+ DCHECK(bitmap);
+ if (!data || data->empty())
+ return false;
+ int width, height;
+ std::vector<unsigned char> decoded_data;
+ if (PNGDecoder::Decode(&data->front(), data->size(), PNGDecoder::FORMAT_BGRA,
+ &decoded_data, &width, &height)) {
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+ memcpy(bitmap->getPixels(), &decoded_data.front(), width * height * 4);
+ return true;
+ }
+ return false;
+}
+
+//static
+SkBitmap* PNGDecoder::CreateSkBitmapFromBGRAFormat(
+ std::vector<unsigned char>& bgra, int width, int height) {
+ SkBitmap* bitmap = new SkBitmap();
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap->allocPixels();
+
+ bool opaque = false;
+ unsigned char* bitmap_data =
+ reinterpret_cast<unsigned char*>(bitmap->getAddr32(0, 0));
+ for (int i = width * height * 4 - 4; i >= 0; i -= 4) {
+ unsigned char alpha = bgra[i + 3];
+ if (!opaque && alpha != 255) {
+ opaque = false;
+ }
+ bitmap_data[i + 3] = alpha;
+ bitmap_data[i] = (bgra[i] * alpha) >> 8;
+ bitmap_data[i + 1] = (bgra[i + 1] * alpha) >> 8;
+ bitmap_data[i + 2] = (bgra[i + 2] * alpha) >> 8;
+ }
+
+ bitmap->setIsOpaque(opaque);
+ return bitmap;
+}
diff --git a/base/gfx/png_decoder.h b/base/gfx/png_decoder.h
new file mode 100644
index 0000000..4912187
--- /dev/null
+++ b/base/gfx/png_decoder.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PNG_DECODER_H__
+#define BASE_GFX_PNG_DECODER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+class SkBitmap;
+
+// Interface for decoding PNG data. This is a wrapper around libpng,
+// which has an inconvenient interface for callers. This is currently designed
+// for use in tests only (where we control the files), so the handling isn't as
+// robust as would be required for a browser (see Decode() for more). WebKit
+// has its own more complicated PNG decoder which handles, among other things,
+// partially downloaded data.
+class PNGDecoder {
+ public:
+ enum ColorFormat {
+ // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+ // This is the native JPEG format.
+ FORMAT_RGB,
+
+ // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
+ FORMAT_RGBA,
+
+ // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
+ // This is the default Windows DIB order.
+ FORMAT_BGRA
+ };
+
+ // Decodes the PNG data contained in input of length input_size. The
+ // decoded data will be placed in *output with the dimensions in *w and *h
+ // on success (returns true). This data will be written in the 'format'
+ // format. On failure, the values of these output variables are undefined.
+ //
+ // This function may not support all PNG types, and it hasn't been tested
+ // with a large number of images, so assume a new format may not work. It's
+ // really designed to be able to read in something written by Encode() above.
+ static bool Decode(const unsigned char* input, size_t input_size,
+ ColorFormat format, std::vector<unsigned char>* output,
+ int* w, int* h);
+
+ // A convenience function for decoding PNGs as previously encoded by the PNG
+ // encoder. Chrome encodes png in the format PNGDecoder::FORMAT_BGRA.
+ //
+ // Returns true if data is non-null and can be decoded as a png, false
+ // otherwise.
+ static bool Decode(const std::vector<unsigned char>* data, SkBitmap* icon);
+
+ // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned
+ // SkBitmap.
+ static SkBitmap* CreateSkBitmapFromBGRAFormat(
+ std::vector<unsigned char>& bgra, int width, int height);
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(PNGDecoder);
+};
+
+#endif // BASE_GFX_PNG_DECODER_H__
diff --git a/base/gfx/png_encoder.cc b/base/gfx/png_encoder.cc
new file mode 100644
index 0000000..ee31a2c
--- /dev/null
+++ b/base/gfx/png_encoder.cc
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/gfx/png_encoder.h"
+#include "base/logging.h"
+
+extern "C" {
+#include "png.h"
+}
+
+namespace {
+
+// Converts BGRA->RGBA and RGBA->BGRA.
+void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width,
+ unsigned char* output) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &input[x * 4];
+ unsigned char* pixel_out = &output[x * 4];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ pixel_out[3] = pixel_in[3];
+ }
+}
+
+void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width,
+ unsigned char* rgb) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &rgba[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[0];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[2];
+ }
+}
+
+} // namespace
+
+// Encoder --------------------------------------------------------------------
+//
+// This section of the code is based on nsPNGEncoder.cpp in Mozilla
+// (Copyright 2005 Google Inc.)
+
+namespace {
+
+// Passed around as the io_ptr in the png structs so our callbacks know where
+// to write data.
+struct PngEncoderState {
+ PngEncoderState(std::vector<unsigned char>* o) : out(o) {}
+ std::vector<unsigned char>* out;
+};
+
+// Called by libpng to flush its internal buffer to ours.
+void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) {
+ PngEncoderState* state = static_cast<PngEncoderState*>(png_get_io_ptr(png));
+ DCHECK(state->out);
+
+ size_t old_size = state->out->size();
+ state->out->resize(old_size + size);
+ memcpy(&(*state->out)[old_size], data, size);
+}
+
+void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width,
+ unsigned char* rgb) {
+ for (int x = 0; x < pixel_width; x++) {
+ const unsigned char* pixel_in = &bgra[x * 4];
+ unsigned char* pixel_out = &rgb[x * 3];
+ pixel_out[0] = pixel_in[2];
+ pixel_out[1] = pixel_in[1];
+ pixel_out[2] = pixel_in[0];
+ }
+}
+
+// Automatically destroys the given write structs on destruction to make
+// cleanup and error handling code cleaner.
+class PngWriteStructDestroyer {
+ public:
+ PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) {
+ }
+ ~PngWriteStructDestroyer() {
+ png_destroy_write_struct(ps_, pi_);
+ }
+ private:
+ png_struct** ps_;
+ png_info** pi_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer);
+};
+
+} // namespace
+
+// static
+bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ bool discard_transparency,
+ std::vector<unsigned char>* output) {
+ // Run to convert an input row into the output row format, NULL means no
+ // conversion is necessary.
+ void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL;
+
+ int input_color_components, output_color_components;
+ int png_output_color_type;
+ switch (format) {
+ case FORMAT_RGB:
+ input_color_components = 3;
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ discard_transparency = false;
+ break;
+
+ case FORMAT_RGBA:
+ input_color_components = 4;
+ if (discard_transparency) {
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ converter = ConvertRGBAtoRGB;
+ } else {
+ output_color_components = 4;
+ png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ converter = NULL;
+ }
+ break;
+
+ case FORMAT_BGRA:
+ input_color_components = 4;
+ if (discard_transparency) {
+ output_color_components = 3;
+ png_output_color_type = PNG_COLOR_TYPE_RGB;
+ converter = ConvertBGRAtoRGB;
+ } else {
+ output_color_components = 4;
+ png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA;
+ converter = ConvertBetweenBGRAandRGBA;
+ }
+ break;
+
+ default:
+ NOTREACHED() << "Unknown pixel format";
+ return false;
+ }
+
+ // Row stride should be at least as long as the length of the data.
+ DCHECK(input_color_components * w <= row_byte_width);
+
+ png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+ png_voidp_NULL,
+ png_error_ptr_NULL,
+ png_error_ptr_NULL);
+ if (!png_ptr)
+ return false;
+ png_info* info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr) {
+ png_destroy_write_struct(&png_ptr, NULL);
+ return false;
+ }
+ PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr);
+
+ if (setjmp(png_jmpbuf(png_ptr))) {
+ // The destroyer will ensure that the structures are cleaned up in this
+ // case, even though we may get here as a jump from random parts of the
+ // PNG library called below.
+ return false;
+ }
+
+ // Set our callback for libpng to give us the data.
+ PngEncoderState state(output);
+ png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL);
+
+ png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type,
+ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
+ PNG_FILTER_TYPE_DEFAULT);
+ png_write_info(png_ptr, info_ptr);
+
+ if (!converter) {
+ // No conversion needed, give the data directly to libpng.
+ for (int y = 0; y < h; y ++)
+ png_write_row(png_ptr, const_cast<unsigned char*>(&input[y * row_byte_width]));
+ } else {
+ // Needs conversion using a separate buffer.
+ unsigned char* row = new unsigned char[w * output_color_components];
+ for (int y = 0; y < h; y ++) {
+ converter(&input[y * row_byte_width], w, row);
+ png_write_row(png_ptr, row);
+ }
+ delete[] row;
+ }
+
+ png_write_end(png_ptr, info_ptr);
+ return true;
+}
diff --git a/base/gfx/png_encoder.h b/base/gfx/png_encoder.h
new file mode 100644
index 0000000..3129775
--- /dev/null
+++ b/base/gfx/png_encoder.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_PNG_ENCODER_H__
+#define BASE_GFX_PNG_ENCODER_H__
+
+#include <vector>
+
+#include "base/basictypes.h"
+
+// Interface for encoding PNG data. This is a wrapper around libpng,
+// which has an inconvenient interface for callers. This is currently designed
+// for use in tests only (where we control the files), so the handling isn't as
+// robust as would be required for a browser (see Decode() for more). WebKit
+// has its own more complicated PNG decoder which handles, among other things,
+// partially downloaded data.
+class PNGEncoder {
+ public:
+ enum ColorFormat {
+ // 3 bytes per pixel (packed), in RGB order regardless of endianness.
+ // This is the native JPEG format.
+ FORMAT_RGB,
+
+ // 4 bytes per pixel, in RGBA order in memory regardless of endianness.
+ FORMAT_RGBA,
+
+ // 4 bytes per pixel, in BGRA order in memory regardless of endianness.
+ // This is the default Windows DIB order.
+ FORMAT_BGRA
+ };
+
+ // Encodes the given raw 'input' data, with each pixel being represented as
+ // given in 'format'. The encoded PNG data will be written into the supplied
+ // vector and true will be returned on success. On failure (false), the
+ // contents of the output buffer are undefined.
+ //
+ // When writing alpha values, the input colors are assumed to be post
+ // multiplied.
+ //
+ // w, h: dimensions of the image
+ // row_byte_width: the width in bytes of each row. This may be greater than
+ // w * bytes_per_pixel if there is extra padding at the end of each row
+ // (often, each row is padded to the next machine word).
+ // discard_transparency: when true, and when the input data format includes
+ // alpha values, these alpha values will be discarded and only RGB will be
+ // written to the resulting file. Otherwise, alpha values in the input
+ // will be preserved.
+ static bool Encode(const unsigned char* input, ColorFormat format,
+ int w, int h, int row_byte_width,
+ bool discard_transparency,
+ std::vector<unsigned char>* output);
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(PNGEncoder);
+};
+
+#endif // BASE_GFX_PNG_ENCODER_H__
diff --git a/base/gfx/point.cc b/base/gfx/point.cc
new file mode 100644
index 0000000..7c435a0
--- /dev/null
+++ b/base/gfx/point.cc
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/point.h"
+
+#include <windows.h>
+
+namespace gfx {
+
+Point::Point() : x_(0), y_(0) {
+}
+
+Point::Point(int x, int y) : x_(x), y_(y) {
+}
+
+Point::Point(const POINT& point) : x_(point.x), y_(point.y) {
+}
+
+POINT Point::ToPOINT() const {
+ POINT p;
+ p.x = x_;
+ p.y = y_;
+ return p;
+}
+
+} // namespace gfx
diff --git a/base/gfx/point.h b/base/gfx/point.h
new file mode 100644
index 0000000..3e09a81
--- /dev/null
+++ b/base/gfx/point.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_POINT_H__
+#define BASE_GFX_POINT_H__
+
+#ifdef UNIT_TEST
+#include <iostream>
+#endif
+
+typedef struct tagPOINT POINT;
+
+namespace gfx {
+
+//
+// A point has an x and y coordinate.
+//
+class Point {
+ public:
+ Point();
+ Point(int x, int y);
+ explicit Point(const POINT& point);
+
+ ~Point() {}
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+
+ void SetPoint(int x, int y) {
+ x_ = x;
+ y_ = y;
+ }
+
+ void set_x(int x) { x_ = x; }
+ void set_y(int y) { y_ = y; }
+
+ bool operator==(const Point& rhs) const {
+ return x_ == rhs.x_ && y_ == rhs.y_;
+ }
+
+ bool operator!=(const Point& rhs) const {
+ return !(*this == rhs);
+ }
+
+ POINT ToPOINT() const;
+
+ private:
+ int x_;
+ int y_;
+};
+
+} // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Point& p) {
+ return out << p.x() << "," << p.y();
+}
+
+#endif // #ifdef UNIT_TEST
+
+#endif // BASE_GFX_POINT_H__
diff --git a/base/gfx/rect.cc b/base/gfx/rect.cc
new file mode 100644
index 0000000..dda7e67
--- /dev/null
+++ b/base/gfx/rect.cc
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/rect.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+
+namespace {
+
+void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) {
+ if (*origin < dst_origin) {
+ *origin = dst_origin;
+ *size = std::min(dst_size, *size);
+ } else {
+ *size = std::min(dst_size, *size);
+ *origin = std::min(dst_origin + dst_size, *origin + *size) - *size;
+ }
+}
+
+} // namespace
+
+namespace gfx {
+
+Rect::Rect() {
+}
+
+Rect::Rect(int width, int height) {
+ set_width(width);
+ set_height(height);
+}
+
+Rect::Rect(int x, int y, int width, int height)
+ : origin_(x, y) {
+ set_width(width);
+ set_height(height);
+}
+
+Rect::Rect(const RECT& r)
+ : origin_(r.left, r.top) {
+ set_width(r.right - r.left);
+ set_height(r.bottom - r.top);
+}
+
+Rect& Rect::operator=(const RECT& r) {
+ origin_.SetPoint(r.left, r.top);
+ set_width(r.right - r.left);
+ set_height(r.bottom - r.top);
+ return *this;
+}
+
+void Rect::set_width(int width) {
+ if (width < 0) {
+ NOTREACHED();
+ width = 0;
+ }
+
+ size_.set_width(width);
+}
+void Rect::set_height(int height) {
+ if (height < 0) {
+ NOTREACHED();
+ height = 0;
+ }
+
+ size_.set_height(height);
+}
+
+void Rect::SetRect(int x, int y, int width, int height) {
+ origin_.SetPoint(x, y);
+ set_width(width);
+ set_height(height);
+}
+
+void Rect::Inset(int horizontal, int vertical) {
+ set_x(x() + horizontal);
+ set_y(y() + vertical);
+ set_width(std::max(width() - (horizontal * 2), 0));
+ set_height(std::max(height() - (vertical * 2), 0));
+}
+
+void Rect::Offset(int horizontal, int vertical) {
+ set_x(x() + horizontal);
+ set_y(y() + vertical);
+}
+
+bool Rect::IsEmpty() const {
+ return width() == 0 || height() == 0;
+}
+
+bool Rect::operator==(const Rect& other) const {
+ return origin_ == other.origin_ && size_ == other.size_;
+}
+
+RECT Rect::ToRECT() const {
+ RECT r;
+ r.left = x();
+ r.right = right();
+ r.top = y();
+ r.bottom = bottom();
+ return r;
+}
+
+bool Rect::Contains(int point_x, int point_y) const {
+ return (point_x >= x()) && (point_x < right()) &&
+ (point_y >= y()) && (point_y < bottom());
+}
+
+bool Rect::Contains(const Rect& rect) const {
+ return (rect.x() >= x() && rect.right() <= right() &&
+ rect.y() >= y() && rect.bottom() <= bottom());
+}
+
+bool Rect::Intersects(const Rect& rect) const {
+ return !(rect.x() >= right() || rect.right() <= x() ||
+ rect.y() >= bottom() || rect.bottom() <= y());
+}
+
+Rect Rect::Intersect(const Rect& rect) const {
+ int rx = std::max(x(), rect.x());
+ int ry = std::max(y(), rect.y());
+ int rr = std::min(right(), rect.right());
+ int rb = std::min(bottom(), rect.bottom());
+
+ if (rx >= rr || ry >= rb)
+ rx = ry = rr = rb = 0; // non-intersecting
+
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Union(const Rect& rect) const {
+ // special case empty rects...
+ if (IsEmpty())
+ return rect;
+ if (rect.IsEmpty())
+ return *this;
+
+ int rx = std::min(x(), rect.x());
+ int ry = std::min(y(), rect.y());
+ int rr = std::max(right(), rect.right());
+ int rb = std::max(bottom(), rect.bottom());
+
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::Subtract(const Rect& rect) const {
+ // boundary cases:
+ if (!Intersects(rect))
+ return *this;
+ if (rect.Contains(*this))
+ return Rect();
+
+ int rx = x();
+ int ry = y();
+ int rr = right();
+ int rb = bottom();
+
+ if (rect.y() <= y() && rect.bottom() >= bottom()) {
+ // complete intersection in the y-direction
+ if (rect.x() <= x()) {
+ rx = rect.right();
+ } else {
+ rr = rect.x();
+ }
+ } else if (rect.x() <= x() && rect.right() >= right()) {
+ // complete intersection in the x-direction
+ if (rect.y() <= y()) {
+ ry = rect.bottom();
+ } else {
+ rb = rect.y();
+ }
+ }
+ return Rect(rx, ry, rr - rx, rb - ry);
+}
+
+Rect Rect::AdjustToFit(const Rect& rect) const {
+ int new_x = x();
+ int new_y = y();
+ int new_width = width();
+ int new_height = height();
+ AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width);
+ AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height);
+ return Rect(new_x, new_y, new_width, new_height);
+}
+
+Point Rect::CenterPoint() const {
+ return Point(x() + (width() + 1) / 2, y() + (height() + 1) / 2);
+}
+
+} // namespace gfx
diff --git a/base/gfx/rect.h b/base/gfx/rect.h
new file mode 100644
index 0000000..486f799
--- /dev/null
+++ b/base/gfx/rect.h
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Defines a simple integer rectangle class. The containment semantics
+// are array-like; that is, the coordinate (x, y) is considered to be
+// contained by the rectangle, but the coordinate (x + width, y) is not.
+// The class will happily let you create malformed rectangles (that is,
+// rectangles with negative width and/or height), but there will be assertions
+// in the operations (such as contain()) to complain in this case.
+
+#ifndef BASE_GFX_RECT_H__
+#define BASE_GFX_RECT_H__
+
+#include "base/gfx/size.h"
+#include "base/gfx/point.h"
+
+typedef struct tagRECT RECT;
+
+namespace gfx {
+
+class Rect {
+ public:
+ Rect();
+ Rect(int width, int height);
+ Rect(int x, int y, int width, int height);
+ explicit Rect(const RECT& r);
+ Rect(const gfx::Point& origin, const gfx::Size& size);
+
+ ~Rect() {}
+
+ Rect& operator=(const RECT& r);
+
+ int x() const { return origin_.x(); }
+ void set_x(int x) { origin_.set_x(x); }
+
+ int y() const { return origin_.y(); }
+ void set_y(int y) { origin_.set_y(y); }
+
+ int width() const { return size_.width(); }
+ void set_width(int width);
+
+ int height() const { return size_.height(); }
+ void set_height(int height);
+
+ const gfx::Point& origin() const { return origin_; }
+ void set_origin(const gfx::Point& origin) { origin_ = origin; }
+
+ const gfx::Size& size() const { return size_; }
+
+ int right() const { return x() + width(); }
+ int bottom() const { return y() + height(); }
+
+ void SetRect(int x, int y, int width, int height);
+
+ // Shrink the rectangle by a horizontal and vertical distance on all sides.
+ void Inset(int horizontal, int vertical);
+
+ // Move the rectangle by a horizontal and vertical distance.
+ void Offset(int horizontal, int vertical);
+
+ // Returns true if the area of the rectangle is zero.
+ bool IsEmpty() const;
+
+ bool operator==(const Rect& other) const;
+
+ bool operator!=(const Rect& other) const {
+ return !(*this == other);
+ }
+
+ // Construct an equivalent Win32 RECT object.
+ RECT ToRECT() const;
+
+ // Returns true if the point identified by point_x and point_y falls inside
+ // this rectangle. The point (x, y) is inside the rectangle, but the
+ // point (x + width, y + height) is not.
+ bool Contains(int point_x, int point_y) const;
+
+ // Returns true if this rectangle contains the specified rectangle.
+ bool Contains(const Rect& rect) const;
+
+ // Returns true if this rectangle intersects the specified rectangle.
+ bool Intersects(const Rect& rect) const;
+
+ // Computes the intersection of this rectangle with the given rectangle.
+ Rect Intersect(const Rect& rect) const;
+
+ // Computes the union of this rectangle with the given rectangle. The union
+ // is the smallest rectangle containing both rectangles.
+ Rect Union(const Rect& rect) const;
+
+ // Computes the rectangle resulting from subtracting |rect| from |this|. If
+ // |rect| does not intersect completely in either the x- or y-direction, then
+ // |*this| is returned. If |rect| contains |this|, then an empty Rect is
+ // returned.
+ Rect Subtract(const Rect& rect) const;
+
+ // Returns true if this rectangle equals that of the supplied rectangle.
+ bool Equals(const Rect& rect) const {
+ return *this == rect;
+ }
+
+ // Fits as much of the receiving rectangle into the supplied rectangle as
+ // possible, returning the result. For example, if the receiver had
+ // a x-location of 2 and a width of 4, and the supplied rectangle had
+ // an x-location of 0 with a width of 5, the returned rectangle would have
+ // an x-location of 1 with a width of 4.
+ Rect AdjustToFit(const Rect& rect) const;
+
+ // Returns the center of this rectangle.
+ Point CenterPoint() const;
+
+ private:
+ gfx::Point origin_;
+ gfx::Size size_;
+};
+
+} // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Rect& r) {
+ return out << r.origin() << " " << r.size();
+}
+
+#endif // #ifdef UNIT_TEST
+
+#endif // BASE_GFX_RECT_H__
diff --git a/base/gfx/rect_unittest.cc b/base/gfx/rect_unittest.cc
new file mode 100644
index 0000000..c218cc9
--- /dev/null
+++ b/base/gfx/rect_unittest.cc
@@ -0,0 +1,295 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/gfx/rect.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+typedef testing::Test RectTest;
+
+TEST(RectTest, Contains) {
+ static const struct ContainsCase {
+ int rect_x;
+ int rect_y;
+ int rect_width;
+ int rect_height;
+ int point_x;
+ int point_y;
+ bool contained;
+ } contains_cases[] = {
+ {0, 0, 10, 10, 0, 0, true},
+ {0, 0, 10, 10, 5, 5, true},
+ {0, 0, 10, 10, 9, 9, true},
+ {0, 0, 10, 10, 5, 10, false},
+ {0, 0, 10, 10, 10, 5, false},
+ {0, 0, 10, 10, -1, -1, false},
+ {0, 0, 10, 10, 50, 50, false},
+ #ifdef NDEBUG
+ {0, 0, -10, -10, 0, 0, false},
+ #endif // NDEBUG
+ };
+ for (int i = 0; i < arraysize(contains_cases); ++i) {
+ const ContainsCase& value = contains_cases[i];
+ gfx::Rect rect(value.rect_x, value.rect_y,
+ value.rect_width, value.rect_height);
+ EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y));
+ }
+}
+
+TEST(RectTest, Intersects) {
+ static const struct {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ bool intersects;
+ } tests[] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, false },
+ { 0, 0, 10, 10, 0, 0, 10, 10, true },
+ { 0, 0, 10, 10, 10, 10, 10, 10, false },
+ { 10, 10, 10, 10, 0, 0, 10, 10, false },
+ { 10, 10, 10, 10, 5, 5, 10, 10, true },
+ { 10, 10, 10, 10, 15, 15, 10, 10, true },
+ { 10, 10, 10, 10, 20, 15, 10, 10, false },
+ { 10, 10, 10, 10, 21, 15, 10, 10, false }
+ };
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ EXPECT_EQ(tests[i].intersects, r1.Intersects(r2));
+ }
+}
+
+TEST(RectTest, Intersect) {
+ static const struct {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: the union of rects 1 and 2
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 0, 0, // zeros
+ 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4, // equal
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 0, 0, 4, 4, // neighboring
+ 4, 4, 4, 4,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4, // overlapping corners
+ 2, 2, 4, 4,
+ 2, 2, 2, 2 },
+ { 0, 0, 4, 4, // T junction
+ 3, 1, 4, 2,
+ 3, 1, 1, 2 },
+ { 3, 0, 2, 2, // gap
+ 0, 0, 2, 2,
+ 0, 0, 0, 0 }
+ };
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect ir = r1.Intersect(r2);
+ EXPECT_EQ(r3.x(), ir.x());
+ EXPECT_EQ(r3.y(), ir.y());
+ EXPECT_EQ(r3.width(), ir.width());
+ EXPECT_EQ(r3.height(), ir.height());
+ }
+}
+
+TEST(RectTest, Union) {
+ static const struct {
+ int x1; // rect 1
+ int y1;
+ int w1;
+ int h1;
+ int x2; // rect 2
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: the union of rects 1 and 2
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0 },
+ { 0, 0, 4, 4,
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 0, 0, 4, 4,
+ 4, 4, 4, 4,
+ 0, 0, 8, 8 },
+ { 0, 0, 4, 4,
+ 0, 5, 4, 4,
+ 0, 0, 4, 9 },
+ { 0, 0, 2, 2,
+ 3, 3, 2, 2,
+ 0, 0, 5, 5 },
+ { 3, 3, 2, 2, // reverse r1 and r2 from previous test
+ 0, 0, 2, 2,
+ 0, 0, 5, 5 },
+ { 0, 0, 0, 0, // union with empty rect
+ 2, 2, 2, 2,
+ 2, 2, 2, 2 }
+ };
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect u = r1.Union(r2);
+ EXPECT_EQ(r3.x(), u.x());
+ EXPECT_EQ(r3.y(), u.y());
+ EXPECT_EQ(r3.width(), u.width());
+ EXPECT_EQ(r3.height(), u.height());
+ }
+}
+
+TEST(RectTest, Equals) {
+ ASSERT_TRUE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 0)));
+ ASSERT_TRUE(gfx::Rect(1, 2, 3, 4).Equals(gfx::Rect(1, 2, 3, 4)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 1)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 1, 0)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 1, 0, 0)));
+ ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(1, 0, 0, 0)));
+}
+
+TEST(RectTest, AdjustToFit) {
+ static const struct {
+ int x1; // source
+ int y1;
+ int w1;
+ int h1;
+ int x2; // target
+ int y2;
+ int w2;
+ int h2;
+ int x3; // rect 3: results of invoking AdjustToFit
+ int y3;
+ int w3;
+ int h3;
+ } tests[] = {
+ { 0, 0, 2, 2,
+ 0, 0, 2, 2,
+ 0, 0, 2, 2 },
+ { 2, 2, 3, 3,
+ 0, 0, 4, 4,
+ 1, 1, 3, 3 },
+ { -1, -1, 5, 5,
+ 0, 0, 4, 4,
+ 0, 0, 4, 4 },
+ { 2, 2, 4, 4,
+ 0, 0, 3, 3,
+ 0, 0, 3, 3 },
+ { 2, 2, 1, 1,
+ 0, 0, 3, 3,
+ 2, 2, 1, 1 }
+ };
+ for (size_t i = 0; i < arraysize(tests); ++i) {
+ gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1);
+ gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2);
+ gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3);
+ gfx::Rect u(r1.AdjustToFit(r2));
+ EXPECT_EQ(r3.x(), u.x());
+ EXPECT_EQ(r3.y(), u.y());
+ EXPECT_EQ(r3.width(), u.width());
+ EXPECT_EQ(r3.height(), u.height());
+ }
+}
+
+TEST(RectText, Subtract) {
+ // Matching
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 10, 20, 20)).Equals(
+ gfx::Rect(0, 0, 0, 0)));
+
+ // Contains
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 30, 30)).Equals(
+ gfx::Rect(0, 0, 0, 0)));
+
+ // No intersection
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(30, 30, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 20)));
+
+ // Not a complete intersection in either direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(15, 15, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 20)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 15, 20, 20)).Equals(
+ gfx::Rect(10, 10, 20, 5)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 15, 30, 20)).Equals(
+ gfx::Rect(10, 10, 20, 5)));
+
+ // Complete intersection in the x-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 30, 20)).Equals(
+ gfx::Rect(10, 25, 20, 5)));
+
+ // Complete intersection in the y-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(10, 10, 10, 30)).Equals(
+ gfx::Rect(20, 10, 10, 20)));
+
+ // Complete intersection in the y-direction
+ EXPECT_TRUE(
+ gfx::Rect(10, 10, 20, 20).Subtract(
+ gfx::Rect(5, 5, 20, 30)).Equals(
+ gfx::Rect(25, 10, 5, 20)));
+}
diff --git a/base/gfx/size.cc b/base/gfx/size.cc
new file mode 100644
index 0000000..153c29e
--- /dev/null
+++ b/base/gfx/size.cc
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/size.h"
+
+#include <windows.h>
+
+#include <ostream>
+
+namespace gfx {
+
+SIZE Size::ToSIZE() const {
+ SIZE s;
+ s.cx = width_;
+ s.cy = height_;
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
+ return out << s.width() << "x" << s.height();
+}
+
+} // namespace gfx
diff --git a/base/gfx/size.h b/base/gfx/size.h
new file mode 100644
index 0000000..cba2e51
--- /dev/null
+++ b/base/gfx/size.h
@@ -0,0 +1,91 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_SIZE_H__
+#define BASE_GFX_SIZE_H__
+
+#ifdef UNIT_TEST
+#include <iostream>
+#endif
+
+typedef struct tagSIZE SIZE;
+
+namespace gfx {
+
+//
+// A size has width and height values.
+//
+class Size {
+ public:
+ Size() : width_(0), height_(0) {}
+ Size(int width, int height) : width_(width), height_(height) {}
+
+ ~Size() {}
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+
+ void SetSize(int width, int height) {
+ width_ = width;
+ height_ = height;
+ }
+
+ void set_width(int width) { width_ = width; }
+ void set_height(int height) { height_ = height; }
+
+ bool operator==(const Size& s) const {
+ return width_ == s.width_ && height_ == s.height_;
+ }
+
+ bool operator!=(const Size& s) const {
+ return !(*this == s);
+ }
+
+ bool IsEmpty() const {
+ return !width_ && !height_;
+ }
+
+ SIZE ToSIZE() const;
+
+ private:
+ int width_;
+ int height_;
+};
+
+} // namespace gfx
+
+#ifdef UNIT_TEST
+
+inline std::ostream& operator<<(std::ostream& out, const gfx::Size& s) {
+ return out << s.width() << "x" << s.height();
+}
+
+#endif // #ifdef UNIT_TEST
+
+#endif // BASE_GFX_SIZE_H__
diff --git a/base/gfx/skia_utils.cc b/base/gfx/skia_utils.cc
new file mode 100644
index 0000000..60e977d
--- /dev/null
+++ b/base/gfx/skia_utils.cc
@@ -0,0 +1,99 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/skia_utils.h"
+
+#include "base/logging.h"
+#include "SkRect.h"
+#include "SkGradientShader.h"
+
+namespace {
+
+COMPILE_ASSERT(offsetof(RECT, left) == offsetof(SkIRect, fLeft), o1);
+COMPILE_ASSERT(offsetof(RECT, top) == offsetof(SkIRect, fTop), o2);
+COMPILE_ASSERT(offsetof(RECT, right) == offsetof(SkIRect, fRight), o3);
+COMPILE_ASSERT(offsetof(RECT, bottom) == offsetof(SkIRect, fBottom), o4);
+COMPILE_ASSERT(sizeof(RECT().left) == sizeof(SkIRect().fLeft), o5);
+COMPILE_ASSERT(sizeof(RECT().top) == sizeof(SkIRect().fTop), o6);
+COMPILE_ASSERT(sizeof(RECT().right) == sizeof(SkIRect().fRight), o7);
+COMPILE_ASSERT(sizeof(RECT().bottom) == sizeof(SkIRect().fBottom), o8);
+COMPILE_ASSERT(sizeof(RECT) == sizeof(SkIRect), o9);
+
+} // namespace
+
+namespace gfx {
+
+POINT SkPointToPOINT(const SkPoint& point) {
+ POINT win_point = { SkScalarRound(point.fX), SkScalarRound(point.fY) };
+ return win_point;
+}
+
+SkRect RECTToSkRect(const RECT& rect) {
+ SkRect sk_rect = { SkIntToScalar(rect.left), SkIntToScalar(rect.top),
+ SkIntToScalar(rect.right), SkIntToScalar(rect.bottom) };
+ return sk_rect;
+}
+
+SkShader* CreateGradientShader(int start_point,
+ int end_point,
+ SkColor start_color,
+ SkColor end_color) {
+ SkColor grad_colors[2] = { start_color, end_color};
+ SkPoint grad_points[2];
+ grad_points[0].set(SkIntToScalar(0), SkIntToScalar(start_point));
+ grad_points[1].set(SkIntToScalar(0), SkIntToScalar(end_point));
+
+ return SkGradientShader::CreateLinear(
+ grad_points, grad_colors, NULL, 2, SkShader::kRepeat_TileMode);
+}
+
+
+SkColor COLORREFToSkColor(COLORREF color) {
+#ifndef _MSC_VER
+ return SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
+#else
+ // ARGB = 0xFF000000 | ((0BGR -> RGB0) >> 8)
+ return 0xFF000000u | (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+COLORREF SkColorToCOLORREF(SkColor color) {
+ // Currently, Alpha is always 255 or the color is 0 so there is no need to
+ // demultiply the channels. If this DCHECK() is ever hit, the full
+ // (SkColorGetX(color) * 255 / a) will have to be added in the conversion.
+ DCHECK((0xFF == SkColorGetA(color)) || (0 == color));
+#ifndef _MSC_VER
+ return RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
+#else
+ // 0BGR = ((ARGB -> BGRA) >> 8)
+ return (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+} // namespace gfx
diff --git a/base/gfx/skia_utils.h b/base/gfx/skia_utils.h
new file mode 100644
index 0000000..989a17e
--- /dev/null
+++ b/base/gfx/skia_utils.h
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_SKIA_UTILS_H__
+#define BASE_GFX_SKIA_UTILS_H__
+
+#include "SkColor.h"
+#include "SkShader.h"
+
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+typedef unsigned long DWORD;
+typedef DWORD COLORREF;
+typedef struct tagPOINT POINT;
+typedef struct tagRECT RECT;
+
+namespace gfx {
+
+// Converts a Skia point to a Windows POINT.
+POINT SkPointToPOINT(const SkPoint& point);
+
+// Converts a Windows RECT to a Skia rect.
+SkRect RECTToSkRect(const RECT& rect);
+
+// Converts a Windows RECT to a Skia rect.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const SkIRect& RECTToSkIRect(const RECT& rect) {
+ return reinterpret_cast<const SkIRect&>(rect);
+}
+
+// Converts a Skia rect to a Windows RECT.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const RECT& SkIRectToRECT(const SkIRect& rect) {
+ return reinterpret_cast<const RECT&>(rect);
+}
+
+// Creates a vertical gradient shader. The caller owns the shader.
+SkShader* CreateGradientShader(int start_point,
+ int end_point,
+ SkColor start_color,
+ SkColor end_color);
+
+// Converts COLORREFs (0BGR) to the ARGB layout Skia expects.
+SkColor COLORREFToSkColor(COLORREF color);
+
+// Converts ARGB to COLORREFs (0BGR).
+COLORREF SkColorToCOLORREF(SkColor color);
+
+} // namespace gfx
+
+#endif
diff --git a/base/gfx/uniscribe.cc b/base/gfx/uniscribe.cc
new file mode 100644
index 0000000..fbfb751
--- /dev/null
+++ b/base/gfx/uniscribe.cc
@@ -0,0 +1,872 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/gfx/uniscribe.h"
+
+#include "base/gfx/font_utils.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+// This function is used to see where word spacing should be applied inside
+// runs. Note that this must match Font::treatAsSpace so we all agree where
+// and how much space this is, so we don't want to do more general Unicode
+// "is this a word break" thing.
+static bool TreatAsSpace(wchar_t c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == 0x00A0;
+}
+
+// SCRIPT_FONTPROPERTIES contains glyph indices for default, invalid
+// and blank glyphs. Just because ScriptShape succeeds does not mean
+// that a text run is rendered correctly. Some characters may be rendered
+// with default/invalid/blank glyphs. Therefore, we need to check if the glyph
+// array returned by ScriptShape contains any of those glyphs to make
+// sure that the text run is rendered successfully.
+static bool ContainsMissingGlyphs(WORD *glyphs,
+ int length,
+ SCRIPT_FONTPROPERTIES* properties) {
+ for (int i = 0; i < length; ++i) {
+ if (glyphs[i] == properties->wgDefault ||
+ (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank))
+ return true;
+ }
+
+ return false;
+}
+
+// HFONT is the 'incarnation' of 'everything' about font, but it's an opaque
+// handle and we can't directly query it to make a new HFONT sharing
+// its characteristics (height, style, etc) except for family name.
+// This function uses GetObject to convert HFONT back to LOGFONT,
+// resets the fields of LOGFONT and calculates style to use later
+// for the creation of a font identical to HFONT other than family name.
+static void SetLogFontAndStyle(HFONT hfont, LOGFONT *logfont, int *style) {
+ DCHECK(hfont && logfont);
+ if (!hfont || !logfont)
+ return;
+
+ GetObject(hfont, sizeof(LOGFONT), logfont);
+ // We reset these fields to values appropriate for CreateFontIndirect.
+ // while keeping lfHeight, which is the most important value in creating
+ // a new font similar to hfont.
+ logfont->lfWidth = 0;
+ logfont->lfEscapement = 0;
+ logfont->lfOrientation = 0;
+ logfont->lfCharSet = DEFAULT_CHARSET;
+ logfont->lfOutPrecision = OUT_TT_ONLY_PRECIS;
+ logfont->lfQuality = DEFAULT_QUALITY; // Honor user's desktop settings.
+ logfont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
+ if (style)
+ *style = gfx::GetStyleFromLogfont(logfont);
+}
+
+UniscribeState::UniscribeState(const wchar_t* input,
+ int input_length,
+ bool is_rtl,
+ HFONT hfont,
+ SCRIPT_CACHE* script_cache,
+ SCRIPT_FONTPROPERTIES* font_properties)
+ : input_(input),
+ input_length_(input_length),
+ is_rtl_(is_rtl),
+ hfont_(hfont),
+ script_cache_(script_cache),
+ font_properties_(font_properties),
+ directional_override_(false),
+ inhibit_ligate_(false),
+ letter_spacing_(0),
+ space_width_(0),
+ word_spacing_(0),
+ ascent_(0) {
+ logfont_.lfFaceName[0] = 0;
+}
+
+UniscribeState::~UniscribeState() {
+}
+
+void UniscribeState::InitWithOptionalLengthProtection(bool length_protection) {
+ // We cap the input length and just don't do anything. We'll allocate a lot
+ // of things of the size of the number of characters, so the allocated memory
+ // will be several times the input length. Plus shaping such a large buffer
+ // may be a form of denial of service. No legitimate text should be this long.
+ // It also appears that Uniscribe flatly rejects very long strings, so we
+ // don't lose anything by doing this.
+ //
+ // The input length protection may be disabled by the unit tests to cause
+ // an error condition.
+ static const int kMaxInputLength = 65535;
+ if (input_length_ == 0 ||
+ (length_protection && input_length_ > kMaxInputLength))
+ return;
+
+ FillRuns();
+ FillShapes();
+ FillScreenOrder();
+}
+
+int UniscribeState::Width() const {
+ int width = 0;
+ for (int item_index = 0; item_index < static_cast<int>(runs_->size());
+ item_index++) {
+ width += AdvanceForItem(item_index);
+ }
+ return width;
+}
+
+void UniscribeState::Justify(int additional_space) {
+ // Count the total number of glyphs we have so we know how big to make the
+ // buffers below.
+ int total_glyphs = 0;
+ for (size_t run = 0; run < runs_->size(); run++) {
+ int run_idx = screen_order_[run];
+ total_glyphs += static_cast<int>(shapes_[run_idx].glyph_length());
+ }
+ if (total_glyphs == 0)
+ return; // Nothing to do.
+
+ // We make one big buffer in screen order of all the glyphs we are drawing
+ // across runs so that the justification function will adjust evenly across
+ // all glyphs.
+ StackVector<SCRIPT_VISATTR, 64> visattr;
+ visattr->resize(total_glyphs);
+ StackVector<int, 64> advances;
+ advances->resize(total_glyphs);
+ StackVector<int, 64> justify;
+ justify->resize(total_glyphs);
+
+ // Build the packed input.
+ int dest_index = 0;
+ for (size_t run = 0; run < runs_->size(); run++) {
+ int run_idx = screen_order_[run];
+ const Shaping& shaping = shapes_[run_idx];
+
+ for (int i = 0; i < shaping.glyph_length(); i++, dest_index++) {
+ memcpy(&visattr[dest_index], &shaping.visattr[i], sizeof(SCRIPT_VISATTR));
+ advances[dest_index] = shaping.advance[i];
+ }
+ }
+
+ // The documentation for ScriptJustify is wrong, the parameter is the space
+ // to add and not the width of the column you want.
+ const int min_kashida = 1; // How do we decide what this should be?
+ ScriptJustify(&visattr[0], &advances[0], total_glyphs, additional_space,
+ min_kashida, &justify[0]);
+
+ // Now we have to unpack the justification amounts back into the runs so
+ // the glyph indices match.
+ int global_glyph_index = 0;
+ for (size_t run = 0; run < runs_->size(); run++) {
+ int run_idx = screen_order_[run];
+ Shaping& shaping = shapes_[run_idx];
+
+ shaping.justify->resize(shaping.glyph_length());
+ for (int i = 0; i < shaping.glyph_length(); i++, global_glyph_index++)
+ shaping.justify[i] = justify[global_glyph_index];
+ }
+}
+
+int UniscribeState::CharacterToX(int offset) const {
+ HRESULT hr;
+ DCHECK(offset <= input_length_);
+
+ // Our algorithm is to traverse the items in screen order from left to
+ // right, adding in each item's screen width until we find the item with
+ // the requested character in it.
+ int width = 0;
+ for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+ // Compute the length of this run.
+ int item_idx = screen_order_[screen_idx];
+ const SCRIPT_ITEM& item = runs_[item_idx];
+ const Shaping& shaping = shapes_[item_idx];
+ int item_length = shaping.char_length();
+
+ if (offset >= item.iCharPos && offset <= item.iCharPos + item_length) {
+ // Character offset is in this run.
+ int char_len = offset - item.iCharPos;
+
+ int cur_x = 0;
+ hr = ScriptCPtoX(char_len, FALSE, item_length, shaping.glyph_length(),
+ &shaping.logs[0], &shaping.visattr[0],
+ shaping.effective_advances(), &item.a, &cur_x);
+ if (FAILED(hr))
+ return 0;
+
+ width += cur_x + shaping.pre_padding;
+ DCHECK(width >= 0);
+ return width;
+ }
+
+ // Move to the next item.
+ width += AdvanceForItem(item_idx);
+ }
+ DCHECK(width >= 0);
+ return width;
+}
+
+int UniscribeState::XToCharacter(int x) const {
+ // We iterate in screen order until we find the item with the given pixel
+ // position in it. When we find that guy, we ask Uniscribe for the
+ // character index.
+ HRESULT hr;
+ for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+ int item_idx = screen_order_[screen_idx];
+ int advance_for_item = AdvanceForItem(item_idx);
+
+ // Note that the run may be empty if shaping failed, so we want to skip
+ // over it.
+ const Shaping& shaping = shapes_[item_idx];
+ int item_length = shaping.char_length();
+ if (x <= advance_for_item && item_length > 0) {
+ // The requested offset is within this item.
+ const SCRIPT_ITEM& item = runs_[item_idx];
+
+ // Account for the leading space we've added to this run that Uniscribe
+ // doesn't know about.
+ x -= shaping.pre_padding;
+
+ int char_x = 0;
+ int trailing;
+ hr = ScriptXtoCP(x, item_length, shaping.glyph_length(),
+ &shaping.logs[0], &shaping.visattr[0],
+ shaping.effective_advances(), &item.a, &char_x,
+ &trailing);
+
+ // The character offset is within the item. We need to add the item's
+ // offset to transform it into the space of the TextRun
+ return char_x + item.iCharPos;
+ }
+
+ // The offset is beyond this item, account for its length and move on.
+ x -= advance_for_item;
+ }
+
+ // Error condition, we don't know what to do if we don't have that X
+ // position in any of our items.
+ return 0;
+}
+
+void UniscribeState::Draw(HDC dc, int x, int y, int from, int to) {
+ HGDIOBJ old_font = 0;
+ int cur_x = x;
+ bool first_run = true;
+
+ for (size_t screen_idx = 0; screen_idx < runs_->size(); screen_idx++) {
+ int item_idx = screen_order_[screen_idx];
+ const SCRIPT_ITEM& item = runs_[item_idx];
+ const Shaping& shaping = shapes_[item_idx];
+
+ // Character offsets within this run. THESE MAY NOT BE IN RANGE and may
+ // be negative, etc. The code below handles this.
+ int from_char = from - item.iCharPos;
+ int to_char = to - item.iCharPos;
+
+ // See if we need to draw any characters in this item.
+ if (shaping.char_length() == 0 ||
+ from_char >= shaping.char_length() || to_char <= 0) {
+ // No chars in this item to display.
+ cur_x += AdvanceForItem(item_idx);
+ continue;
+ }
+
+ // Compute the starting glyph within this span. |from| and |to| are
+ // global offsets that may intersect arbitrarily with our local run.
+ int from_glyph, after_glyph;
+ if (item.a.fRTL) {
+ // To compute the first glyph when going RTL, we use |to|.
+ if (to_char >= shaping.char_length()) {
+ // The end of the text is after (to the left) of us.
+ from_glyph = 0;
+ } else {
+ // Since |to| is exclusive, the first character we draw on the left
+ // is actually the one right before (to the right) of |to|.
+ from_glyph = shaping.logs[to_char - 1];
+ }
+
+ // The last glyph is actually the first character in the range.
+ if (from_char <= 0) {
+ // The first character to draw is before (to the right) of this span,
+ // so draw all the way to the end.
+ after_glyph = shaping.glyph_length();
+ } else {
+ // We want to draw everything up until the character to the right of
+ // |from|. To the right is - 1, so we look that up (remember our
+ // character could be more than one glyph, so we can't look up our
+ // glyph and add one).
+ after_glyph = shaping.logs[from_char - 1];
+ }
+ } else {
+ // Easy case, everybody agrees about directions. We only need to handle
+ // boundary conditions to get a range inclusive at the beginning, and
+ // exclusive at the ending. We have to do some computation to see the
+ // glyph one past the end.
+ from_glyph = shaping.logs[from_char < 0 ? 0 : from_char];
+ if (to_char >= shaping.char_length())
+ after_glyph = shaping.glyph_length();
+ else
+ after_glyph = shaping.logs[to_char];
+ }
+
+ // Account for the characters that were skipped in this run. When
+ // WebKit asks us to draw a subset of the run, it actually tells us
+ // to draw at the X offset of the beginning of the run, since it
+ // doesn't know the internal position of any of our characters.
+ const int* effective_advances = shaping.effective_advances();
+ int inner_offset = 0;
+ for (int i = 0; i < from_glyph; i++)
+ inner_offset += effective_advances[i];
+
+ // Actually draw the glyphs we found.
+ int glyph_count = after_glyph - from_glyph;
+ if (from_glyph >= 0 && glyph_count > 0) {
+ // Account for the preceeding space we need to add to this run. We don't
+ // need to count for the following space because that will be counted
+ // in AdvanceForItem below when we move to the next run.
+ inner_offset += shaping.pre_padding;
+
+ // Pass NULL in when there is no justification.
+ const int* justify = shaping.justify->empty() ?
+ NULL : &shaping.justify[from_glyph];
+
+ if (first_run) {
+ old_font = SelectObject(dc, shaping.hfont_);
+ first_run = false;
+ } else {
+ SelectObject(dc, shaping.hfont_);
+ }
+
+ // TODO(brettw) bug 698452: if a half a character is selected,
+ // we should set up a clip rect so we draw the half of the glyph
+ // correctly.
+ // Fonts with different ascents can be used to render different runs.
+ // 'Across-runs' y-coordinate correction needs to be adjusted
+ // for each font.
+ HRESULT hr = S_FALSE;
+ for (int executions = 0; executions < 2; ++executions) {
+ hr = ScriptTextOut(dc, shaping.script_cache_, cur_x + inner_offset,
+ y - shaping.ascent_offset_, 0, NULL, &item.a, NULL,
+ 0, &shaping.glyphs[from_glyph],
+ glyph_count, &shaping.advance[from_glyph],
+ justify, &shaping.offsets[from_glyph]);
+ if (S_OK != hr && 0 == executions) {
+ // If this ScriptTextOut is called from the renderer it might fail
+ // because the sandbox is preventing it from opening the font files.
+ // If we are running in the renderer, TryToPreloadFont is overridden
+ // to ask the browser to preload the font for us so we can access it.
+ TryToPreloadFont(shaping.hfont_);
+ continue;
+ }
+ break;
+ }
+
+ DCHECK(S_OK == hr);
+
+
+ }
+
+ cur_x += AdvanceForItem(item_idx);
+ }
+
+ if (old_font)
+ SelectObject(dc, old_font);
+}
+
+WORD UniscribeState::FirstGlyphForCharacter(int char_offset) const {
+ // Find the run for the given character.
+ for (int i = 0; i < static_cast<int>(runs_->size()); i++) {
+ int first_char = runs_[i].iCharPos;
+ const Shaping& shaping = shapes_[i];
+ int local_offset = char_offset - first_char;
+ if (local_offset >= 0 && local_offset < shaping.char_length()) {
+ // The character is in this run, return the first glyph for it (should
+ // generally be the only glyph). It seems Uniscribe gives glyph 0 for
+ // empty, which is what we want to return in the "missing" case.
+ size_t glyph_index = shaping.logs[local_offset];
+ if (glyph_index >= shaping.glyphs->size()) {
+ // The glyph should be in this run, but the run has too few actual
+ // characters. This can happen when shaping the run fails, in which
+ // case, we should have no data in the logs at all.
+ DCHECK(shaping.glyphs->empty());
+ return 0;
+ }
+ return shaping.glyphs[glyph_index];
+ }
+ }
+ return 0;
+}
+
+void UniscribeState::FillRuns() {
+ HRESULT hr;
+ runs_->resize(UNISCRIBE_STATE_STACK_RUNS);
+
+ SCRIPT_STATE input_state;
+ input_state.uBidiLevel = is_rtl_;
+ input_state.fOverrideDirection = directional_override_;
+ input_state.fInhibitSymSwap = false;
+ input_state.fCharShape = false; // Not implemented in Uniscribe
+ input_state.fDigitSubstitute = false; // Do we want this for Arabic?
+ input_state.fInhibitLigate = inhibit_ligate_;
+ input_state.fDisplayZWG = false; // Don't draw control characters.
+ input_state.fArabicNumContext = is_rtl_; // Do we want this for Arabic?
+ input_state.fGcpClusters = false;
+ input_state.fReserved = 0;
+ input_state.fEngineReserved = 0;
+ // The psControl argument to ScriptItemize should be non-NULL for RTL text,
+ // per http://msdn.microsoft.com/en-us/library/ms776532.aspx . So use a
+ // SCRIPT_CONTROL that is set to all zeros. Zero as a locale ID means the
+ // neutral locale per http://msdn.microsoft.com/en-us/library/ms776294.aspx .
+ static SCRIPT_CONTROL input_control = {0, // uDefaultLanguage :16;
+ 0, // fContextDigits :1;
+ 0, // fInvertPreBoundDir :1;
+ 0, // fInvertPostBoundDir :1;
+ 0, // fLinkStringBefore :1;
+ 0, // fLinkStringAfter :1;
+ 0, // fNeutralOverride :1;
+ 0, // fNumericOverride :1;
+ 0, // fLegacyBidiClass :1;
+ 0, // fMergeNeutralItems :1;
+ 0};// fReserved :7;
+ // Calling ScriptApplyDigitSubstitution( NULL, &input_control, &input_state)
+ // here would be appropriate if we wanted to set the language ID, and get
+ // local digit substitution behavior. For now, don't do it.
+
+ while (true) {
+ int num_items = 0;
+
+ // Ideally, we would have a way to know the runs before and after this
+ // one, and put them into the control parameter of ScriptItemize. This
+ // would allow us to shape characters properly that cross style
+ // boundaries (WebKit bug 6148).
+ //
+ // We tell ScriptItemize that the output list of items is one smaller
+ // than it actually is. According to Mozilla bug 366643, if there is
+ // not enough room in the array on pre-SP2 systems, ScriptItemize will
+ // write one past the end of the buffer.
+ //
+ // ScriptItemize is very strange. It will often require a much larger
+ // ITEM buffer internally than it will give us as output. For example,
+ // it will say a 16-item buffer is not big enough, and will write
+ // interesting numbers into all those items. But when we give it a 32
+ // item buffer and it succeeds, it only has one item output.
+ //
+ // It seems to be doing at least two passes, the first where it puts a
+ // lot of intermediate data into our items, and the second where it
+ // collates them.
+ hr = ScriptItemize(input_, input_length_,
+ static_cast<int>(runs_->size()) - 1, &input_control, &input_state,
+ &runs_[0], &num_items);
+ if (SUCCEEDED(hr)) {
+ runs_->resize(num_items);
+ break;
+ }
+ if (hr != E_OUTOFMEMORY) {
+ // Some kind of unexpected error.
+ runs_->resize(0);
+ break;
+ }
+ // There was not enough items for it to write into, expand.
+ runs_->resize(runs_->size() * 2);
+ }
+
+ // Fix up the directions of the items so they're what WebKit thinks
+ // they are. WebKit (and we assume any other caller) always knows what
+ // direction it wants things to be in, and will only give us runs that are in
+ // the same direction. Sometimes, Uniscibe disagrees, for example, if you
+ // have embedded ASCII punctuation in an Arabic string, WebKit will
+ // (correctly) know that is should still be rendered RTL, but Uniscibe might
+ // think LTR is better.
+ //
+ // TODO(brettw) bug 747235:
+ // This workaround fixes the bug but causes spacing problems in other cases.
+ // WebKit sometimes gives us a big run that includes ASCII and Arabic, and
+ // this forcing direction makes those cases incorrect. This seems to happen
+ // during layout only, so it ends up that spacing is incorrect (because being
+ // the wrong direction changes ligatures and stuff).
+ //
+ //for (size_t i = 0; i < runs_->size(); i++)
+ // runs_[i].a.fRTL = is_rtl_;
+}
+
+
+bool UniscribeState::Shape(const wchar_t* input,
+ int item_length,
+ int num_glyphs,
+ SCRIPT_ITEM& run,
+ Shaping& shaping) {
+ HFONT hfont = hfont_;
+ SCRIPT_CACHE* script_cache = script_cache_;
+ SCRIPT_FONTPROPERTIES* font_properties = font_properties_;
+ int ascent = ascent_;
+ HDC temp_dc = NULL;
+ HGDIOBJ old_font = 0;
+ HRESULT hr;
+ bool lastFallbackTried = false;
+ bool result;
+
+ int generated_glyphs = 0;
+
+ // In case HFONT passed in ctor cannot render this run, we have to scan
+ // other fonts from the beginning of the font list.
+ ResetFontIndex();
+
+ // Compute shapes.
+ while (true) {
+ shaping.logs->resize(item_length);
+ shaping.glyphs->resize(num_glyphs);
+ shaping.visattr->resize(num_glyphs);
+
+ // Firefox sets SCRIPT_ANALYSIS.SCRIPT_STATE.fDisplayZWG to true
+ // here. Is that what we want? It will display control characters.
+ hr = ScriptShape(temp_dc, script_cache, input, item_length,
+ num_glyphs, &run.a,
+ &shaping.glyphs[0], &shaping.logs[0],
+ &shaping.visattr[0], &generated_glyphs);
+ if (hr == E_PENDING) {
+ // Allocate the DC.
+ temp_dc = GetDC(NULL);
+ old_font = SelectObject(temp_dc, hfont);
+ continue;
+ } else if (hr == E_OUTOFMEMORY) {
+ num_glyphs *= 2;
+ continue;
+ } else if (SUCCEEDED(hr) &&
+ (lastFallbackTried || !ContainsMissingGlyphs(&shaping.glyphs[0],
+ generated_glyphs, font_properties))) {
+ break;
+ }
+
+ // The current font can't render this run. clear DC and try
+ // next font.
+ if (temp_dc) {
+ SelectObject(temp_dc, old_font);
+ ReleaseDC(NULL, temp_dc);
+ temp_dc = NULL;
+ }
+
+ if (NextWinFontData(&hfont, &script_cache, &font_properties, &ascent)) {
+ // The primary font does not support this run. Try next font.
+ // In case of web page rendering, they come from fonts specified in
+ // CSS stylesheets.
+ continue;
+ } else if (!lastFallbackTried) {
+ lastFallbackTried = true;
+
+ // Generate a last fallback font based on the script of
+ // a character to draw while inheriting size and styles
+ // from the primary font
+ if (!logfont_.lfFaceName[0])
+ SetLogFontAndStyle(hfont_, &logfont_, &style_);
+
+ // TODO(jungshik): generic type should come from webkit for
+ // UniscribeStateTextRun (a derived class used in webkit).
+ const wchar_t *family = GetFallbackFamily(input, item_length,
+ GENERIC_FAMILY_STANDARD);
+ bool font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
+
+ if (!font_ok) {
+ // If this GetDerivedFontData is called from the renderer it might fail
+ // because the sandbox is preventing it from opening the font files.
+ // If we are running in the renderer, TryToPreloadFont is overridden to
+ // ask the browser to preload the font for us so we can access it.
+ TryToPreloadFont(hfont);
+
+ // Try again.
+ font_ok = GetDerivedFontData(family, style_, &logfont_, &ascent, &hfont, &script_cache);
+ DCHECK(font_ok);
+ }
+
+ // TODO(jungshik) : Currently GetDerivedHFont always returns a
+ // a valid HFONT, but in the future, I may change it to return 0.
+ DCHECK(hfont);
+
+ // We don't need a font_properties for the last resort fallback font
+ // because we don't have anything more to try and are forced to
+ // accept empty glyph boxes. If we tried a series of fonts as
+ // 'last-resort fallback', we'd need it, but currently, we don't.
+ continue;
+ } else if (hr == USP_E_SCRIPT_NOT_IN_FONT) {
+ run.a.eScript = SCRIPT_UNDEFINED;
+ continue;
+ } else if (FAILED(hr)) {
+ // Error shaping.
+ generated_glyphs = 0;
+ result = false;
+ goto cleanup;
+ }
+ }
+
+ // Sets Windows font data for this run to those corresponding to
+ // a font supporting this run. we don't need to store font_properties
+ // because it's not used elsewhere.
+ shaping.hfont_ = hfont;
+ shaping.script_cache_ = script_cache;
+
+ // The ascent of a font for this run can be different from
+ // that of the primary font so that we need to keep track of
+ // the difference per run and take that into account when calling
+ // ScriptTextOut in |Draw|. Otherwise, different runs rendered by
+ // different fonts would not be aligned vertically.
+ shaping.ascent_offset_ = ascent_ ? ascent - ascent_ : 0;
+ result = true;
+
+cleanup:
+ shaping.glyphs->resize(generated_glyphs);
+ shaping.visattr->resize(generated_glyphs);
+ shaping.advance->resize(generated_glyphs);
+ shaping.offsets->resize(generated_glyphs);
+ if (temp_dc) {
+ SelectObject(temp_dc, old_font);
+ ReleaseDC(NULL, temp_dc);
+ }
+ // On failure, our logs don't mean anything, so zero those out.
+ if (!result)
+ shaping.logs->clear();
+
+ return result;
+}
+
+void UniscribeState::FillShapes() {
+ shapes_->resize(runs_->size());
+ for (size_t i = 0; i < runs_->size(); i++) {
+ int start_item = runs_[i].iCharPos;
+ int item_length = input_length_ - start_item;
+ if (i < runs_->size() - 1)
+ item_length = runs_[i + 1].iCharPos - start_item;
+
+ int num_glyphs;
+ if (item_length < UNISCRIBE_STATE_STACK_CHARS) {
+ // We'll start our buffer sizes with the current stack space available
+ // in our buffers if the current input fits. As long as it
+ // doesn't expand past that we'll save a lot of time mallocing.
+ num_glyphs = UNISCRIBE_STATE_STACK_CHARS;
+ } else {
+ // When the input doesn't fit, give up with the stack since it will
+ // almost surely not be enough room (unless the input actually shrinks,
+ // which is unlikely) and just start with the length recommended by
+ // the Uniscribe documentation as a "usually fits" size.
+ num_glyphs = item_length * 3 / 2 + 16;
+ }
+
+ // Convert a string to a glyph string trying the primary font,
+ // fonts in the fallback list and then script-specific last resort font.
+ Shaping& shaping = shapes_[i];
+ if (!Shape(&input_[start_item], item_length, num_glyphs, runs_[i], shaping))
+ continue;
+
+ // Compute placements. Note that offsets is documented incorrectly
+ // and is actually an array.
+
+ // DC that we lazily create if Uniscribe commands us to.
+ // (this does not happen often because script_cache is already
+ // updated when calling ScriptShape).
+ HDC temp_dc = NULL;
+ HGDIOBJ old_font = NULL;
+ HRESULT hr;
+ while (true) {
+ shaping.pre_padding = 0;
+ hr = ScriptPlace(temp_dc, shaping.script_cache_, &shaping.glyphs[0],
+ static_cast<int>(shaping.glyphs->size()),
+ &shaping.visattr[0], &runs_[i].a,
+ &shaping.advance[0], &shaping.offsets[0],
+ &shaping.abc);
+ if (hr != E_PENDING)
+ break;
+
+ // Allocate the DC and run the loop again.
+ temp_dc = GetDC(NULL);
+ old_font = SelectObject(temp_dc, shaping.hfont_);
+ }
+
+ if (FAILED(hr)) {
+ // Some error we don't know how to handle. Nuke all of our data
+ // since we can't deal with partially valid data later.
+ runs_->clear();
+ shapes_->clear();
+ screen_order_->clear();
+ }
+
+ if (temp_dc) {
+ SelectObject(temp_dc, old_font);
+ ReleaseDC(NULL, temp_dc);
+ }
+ }
+
+ AdjustSpaceAdvances();
+
+ if (letter_spacing_ != 0 || word_spacing_ != 0)
+ ApplySpacing();
+}
+
+void UniscribeState::FillScreenOrder() {
+ screen_order_->resize(runs_->size());
+
+ // We assume that the input has only one text direction in it.
+ // TODO(brettw) are we sure we want to keep this restriction?
+ if (is_rtl_) {
+ for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
+ screen_order_[static_cast<int>(screen_order_->size()) - i - 1] = i;
+ } else {
+ for (int i = 0; i < static_cast<int>(screen_order_->size()); i++)
+ screen_order_[i] = i;
+ }
+}
+
+void UniscribeState::AdjustSpaceAdvances() {
+ if (space_width_ == 0)
+ return;
+
+ int space_width_without_letter_spacing = space_width_ - letter_spacing_;
+
+ // This mostly matches what WebKit's UniscribeController::shapeAndPlaceItem.
+ for (size_t run = 0; run < runs_->size(); run++) {
+ Shaping& shaping = shapes_[run];
+
+ for (int i = 0; i < shaping.char_length(); i++) {
+ if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
+ continue;
+
+ int glyph_index = shaping.logs[i];
+ int current_advance = shaping.advance[glyph_index];
+ // Don't give zero-width spaces a width.
+ if (!current_advance)
+ continue;
+
+ // current_advance does not include additional letter-spacing, but
+ // space_width does. Here we find out how off we are from the correct
+ // width for the space not including letter-spacing, then just subtract
+ // that diff.
+ int diff = current_advance - space_width_without_letter_spacing;
+ // The shaping can consist of a run of text, so only subtract the
+ // difference in the width of the glyph.
+ shaping.advance[glyph_index] -= diff;
+ shaping.abc.abcB -= diff;
+ }
+ }
+}
+
+void UniscribeState::ApplySpacing() {
+ for (size_t run = 0; run < runs_->size(); run++) {
+ Shaping& shaping = shapes_[run];
+ bool is_rtl = runs_[run].a.fRTL;
+
+ if (letter_spacing_ != 0) {
+ // RTL text gets padded to the left of each character. We increment the
+ // run's advance to make this happen. This will be balanced out by NOT
+ // adding additional advance to the last glyph in the run.
+ if (is_rtl)
+ shaping.pre_padding += letter_spacing_;
+
+ // Go through all the glyphs in this run and increase the "advance" to
+ // account for letter spacing. We adjust letter spacing only on cluster
+ // boundaries.
+ //
+ // This works for most scripts, but may have problems with some indic
+ // scripts. This behavior is better than Firefox or IE for Hebrew.
+ for (int i = 0; i < shaping.glyph_length(); i++) {
+ if (shaping.visattr[i].fClusterStart) {
+ // Ick, we need to assign the extra space so that the glyph comes
+ // first, then is followed by the space. This is opposite for RTL.
+ if (is_rtl) {
+ if (i != shaping.glyph_length() - 1) {
+ // All but the last character just get the spacing applied to
+ // their advance. The last character doesn't get anything,
+ shaping.advance[i] += letter_spacing_;
+ shaping.abc.abcB += letter_spacing_;
+ }
+ } else {
+ // LTR case is easier, we just add to the advance.
+ shaping.advance[i] += letter_spacing_;
+ shaping.abc.abcB += letter_spacing_;
+ }
+ }
+ }
+ }
+
+ // Go through all the characters to find whitespace and insert the extra
+ // wordspacing amount for the glyphs they correspond to.
+ if (word_spacing_ != 0) {
+ for (int i = 0; i < shaping.char_length(); i++) {
+ if (!TreatAsSpace(input_[runs_[run].iCharPos + i]))
+ continue;
+
+ // The char in question is a word separator...
+ int glyph_index = shaping.logs[i];
+
+ // Spaces will not have a glyph in Uniscribe, it will just add
+ // additional advance to the character to the left of the space. The
+ // space's corresponding glyph will be the character following it in
+ // reading order.
+ if (is_rtl) {
+ // In RTL, the glyph to the left of the space is the same as the
+ // first glyph of the following character, so we can just increment
+ // it.
+ shaping.advance[glyph_index] += word_spacing_;
+ shaping.abc.abcB += word_spacing_;
+ } else {
+ // LTR is actually more complex here, we apply it to the previous
+ // character if there is one, otherwise we have to apply it to the
+ // leading space of the run.
+ if (glyph_index == 0) {
+ shaping.pre_padding += word_spacing_;
+ } else {
+ shaping.advance[glyph_index - 1] += word_spacing_;
+ shaping.abc.abcB += word_spacing_;
+ }
+ }
+ }
+ } // word_spacing_ != 0
+
+ // Loop for next run...
+ }
+}
+
+// The advance is the ABC width of the run
+int UniscribeState::AdvanceForItem(int item_index) const {
+ int accum = 0;
+ const Shaping& shaping = shapes_[item_index];
+
+ if (shaping.justify->empty()) {
+ // Easy case with no justification, the width is just the ABC width of t
+ // the run. (The ABC width is the sum of the advances).
+ return shaping.abc.abcA + shaping.abc.abcB + shaping.abc.abcC +
+ shaping.pre_padding;
+ }
+
+ // With justification, we use the justified amounts instead. The
+ // justification array contains both the advance and the extra space
+ // added for justification, so is the width we want.
+ int justification = 0;
+ for (size_t i = 0; i < shaping.justify->size(); i++)
+ justification += shaping.justify[i];
+
+ return shaping.pre_padding + justification;
+}
+
+} // namespace gfx
diff --git a/base/gfx/uniscribe.h b/base/gfx/uniscribe.h
new file mode 100644
index 0000000..29f0d811
--- /dev/null
+++ b/base/gfx/uniscribe.h
@@ -0,0 +1,390 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A wrapper around Uniscribe that provides a reasonable API.
+
+#ifndef BASE_GFX_UNISCRIBE_H__
+#define BASE_GFX_UNISCRIBE_H__
+
+#include <windows.h>
+#include <usp10.h>
+#include <wchar.h>
+#include <map>
+#include <vector>
+
+#include "base/stack_container.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+namespace gfx {
+
+#define UNISCRIBE_STATE_STACK_RUNS 8
+#define UNISCRIBE_STATE_STACK_CHARS 32
+
+// This object should be safe to create & destroy frequently, as long as the
+// caller preserves the script_cache when possible (this data may be slow to
+// compute).
+//
+// This object is "kind of large" (~1K) because it reserves a lot of space for
+// working with to avoid expensive heap operations. Therefore, not only should
+// you not worry about creating and destroying it, you should try to not keep
+// them around.
+class UniscribeState {
+ public:
+ // Initializes this Uniscribe run with the text pointed to by |run| with
+ // |length|. The input is NOT null terminated.
+ //
+ // The is_rtl flag should be set if the input script is RTL. It is assumed
+ // that the caller has already divided up the input text (using ICU, for
+ // example) into runs of the same direction of script. This avoids
+ // disagreements between the caller and Uniscribe later (see FillItems).
+ //
+ // A script cache should be provided by the caller that is initialized to
+ // NULL. When the caller is done with the cache (it may be stored between
+ // runs as long as it is used consistently with the same HFONT), it should
+ // call ScriptFreeCache().
+ UniscribeState(const wchar_t* input,
+ int input_length,
+ bool is_rtl,
+ HFONT hfont,
+ SCRIPT_CACHE* script_cache,
+ SCRIPT_FONTPROPERTIES* font_properties);
+
+ virtual ~UniscribeState();
+
+ // Sets Uniscribe's directional override flag. False by default.
+ bool directional_override() const {
+ return directional_override_;
+ }
+ void set_directional_override(bool override) {
+ directional_override_ = override;
+ }
+
+ // Set's Uniscribe's no-ligate override flag. False by default.
+ bool inhibit_ligate() const {
+ return inhibit_ligate_;
+ }
+ void set_inhibit_ligate(bool inhibit) {
+ inhibit_ligate_ = inhibit;
+ }
+
+ // Set letter spacing. We will try to insert this much space between
+ // graphemes (one or more glyphs perceived as a single unit by ordinary users
+ // of a script). Positive values increase letter spacing, negative values
+ // decrease it. 0 by default.
+ int letter_spacing() const {
+ return letter_spacing_;
+ }
+ void set_letter_spacing(int letter_spacing) {
+ letter_spacing_ = letter_spacing;
+ }
+
+ // Set the width of a standard space character. We use this to normalize
+ // space widths. Windows will make spaces after Hindi characters larger than
+ // other spaces. A space_width of 0 means to use the default space width.
+ //
+ // Must be set before Init() is called.
+ int space_width() const {
+ return space_width_;
+ }
+ void set_space_width(int space_width) {
+ space_width_ = space_width;
+ }
+
+ // Set word spacing. We will try to insert this much extra space between
+ // each word in the input (beyond whatever whitespace character separates
+ // words). Positive values lead to increased letter spacing, negative values
+ // decrease it. 0 by default.
+ //
+ // Must be set before Init() is called.
+ int word_spacing() const {
+ return word_spacing_;
+ }
+ void set_word_spacing(int word_spacing) {
+ word_spacing_ = word_spacing;
+ }
+ void set_ascent(int ascent) {
+ ascent_ = ascent;
+ }
+
+ // You must call this after setting any options but before doing any
+ // other calls like asking for widths or drawing.
+ void Init() { InitWithOptionalLengthProtection(true); }
+
+ // Returns the total width in pixels of the text run.
+ int Width() const;
+
+ // Call to justify the text, with the amount of space that should be ADDED to
+ // get the desired width that the column should be justified to. Normally,
+ // spaces are inserted, but for Arabic there will be kashidas (extra strokes)
+ // inserted instead.
+ //
+ // This function MUST be called AFTER Init().
+ void Justify(int additional_space);
+
+ // Computes the given character offset into a pixel offset of the beginning
+ // of that character.
+ int CharacterToX(int offset) const;
+
+ // Converts the given pixel X position into a logical character offset into
+ // the run. For positions appearing before the first character, this will
+ // return -1.
+ int XToCharacter(int x) const;
+
+ // Draws the given characters to (x, y) in the given DC. The font will be
+ // handled by this function, but the font color and other attributes should
+ // be pre-set.
+ //
+ // The y position is the upper left corner, NOT the baseline.
+ void Draw(HDC dc, int x, int y, int from, int to);
+
+ // Returns the first glyph assigned to the character at the given offset.
+ // This function is used to retrieve glyph information when Uniscribe is
+ // being used to generate glyphs for non-complex, non-BMP (above U+FFFF)
+ // characters. These characters are not otherwise special and have no
+ // complex shaping rules, so we don't otherwise need Uniscribe, except
+ // Uniscribe is the only way to get glyphs for non-BMP characters.
+ //
+ // Returns 0 if there is no glyph for the given character.
+ WORD FirstGlyphForCharacter(int char_offset) const;
+
+ protected:
+ // Backend for init. The flag allows the unit test to specify whether we
+ // should fail early for very long strings like normal, or try to pass the
+ // long string to Uniscribe. The latter provides a way to force failure of
+ // shaping.
+ void InitWithOptionalLengthProtection(bool length_protection);
+
+ // Tries to preload the font when the it is not accessible.
+ // This is the default implementation and it does not do anything.
+ virtual void TryToPreloadFont(HFONT font) {}
+
+ private:
+ FRIEND_TEST(UniscribeTest, TooBig);
+
+ // An array corresponding to each item in runs_ containing information
+ // on each of the glyphs that were generated. Like runs_, this is in
+ // reading order. However, for rtl text, the characters within each
+ // item will be reversed.
+ struct Shaping {
+ Shaping()
+ : pre_padding(0),
+ hfont_(NULL),
+ script_cache_(NULL),
+ ascent_offset_(0) {
+ abc.abcA = 0;
+ abc.abcB = 0;
+ abc.abcC = 0;
+ }
+
+ // Returns the number of glyphs (which will be drawn to the screen)
+ // in this run.
+ int glyph_length() const {
+ return static_cast<int>(glyphs->size());
+ }
+
+ // Returns the number of characters (that we started with) in this run.
+ int char_length() const {
+ return static_cast<int>(logs->size());
+ }
+
+ // Returns the advance array that should be used when measuring glyphs.
+ // The returned pointer will indicate an array with glyph_length() elements
+ // and the advance that should be used for each one. This is either the
+ // real advance, or the justified advances if there is one, and is the
+ // array we want to use for measurement.
+ const int* effective_advances() const {
+ if (advance->empty())
+ return 0;
+ if (justify->empty())
+ return &advance[0];
+ return &justify[0];
+ }
+
+ // This is the advance amount of space that we have added to the beginning
+ // of the run. It is like the ABC's |A| advance but one that we create and
+ // must handle internally whenever computing with pixel offsets.
+ int pre_padding;
+
+ // Glyph indices in the font used to display this item. These indices
+ // are in screen order.
+ StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> glyphs;
+
+ // For each input character, this tells us the first glyph index it
+ // generated. This is the only array with size of the input chars.
+ //
+ // All offsets are from the beginning of this run. Multiple characters can
+ // generate one glyph, in which case there will be adjacent duplicates in
+ // this list. One character can also generate multiple glyphs, in which
+ // case there will be skipped indices in this list.
+ StackVector<WORD, UNISCRIBE_STATE_STACK_CHARS> logs;
+
+ // Flags and such for each glyph.
+ StackVector<SCRIPT_VISATTR, UNISCRIBE_STATE_STACK_CHARS> visattr;
+
+ // Horizontal advances for each glyph listed above, this is basically
+ // how wide each glyph is.
+ StackVector<int, UNISCRIBE_STATE_STACK_CHARS> advance;
+
+ // This contains glyph offsets, from the nominal position of a glyph. It
+ // is used to adjust the positions of multiple combining characters
+ // around/above/below base characters in a context-sensitive manner so
+ // that they don't bump against each other and the base character.
+ StackVector<GOFFSET, UNISCRIBE_STATE_STACK_CHARS> offsets;
+
+ // Filled by a call to Justify, this is empty for nonjustified text.
+ // If nonempty, this contains the array of justify characters for each
+ // character as returned by ScriptJustify.
+ //
+ // This is the same as the advance array, but with extra space added for
+ // some characters. The difference between a glyph's |justify| width and
+ // it's |advance| width is the extra space added.
+ StackVector<int, UNISCRIBE_STATE_STACK_CHARS> justify;
+
+ // Sizing information for this run. This treats the entire run as a
+ // character with a preceeding advance, width, and ending advance.
+ // The B width is the sum of the |advance| array, and the A and C widths
+ // are any extra spacing applied to each end.
+ //
+ // It is unclear from the documentation what this actually means. From
+ // experimentation, it seems that the sum of the character advances is
+ // always the sum of the ABC values, and I'm not sure what you're supposed
+ // to do with the ABC values.
+ ABC abc;
+
+ // Pointers to windows font data used to render this run.
+ HFONT hfont_;
+ SCRIPT_CACHE* script_cache_;
+
+ // Ascent offset between the ascent of the primary font
+ // and that of the fallback font. The offset needs to be applied,
+ // when drawing a string, to align multiple runs rendered with
+ // different fonts.
+ int ascent_offset_;
+ };
+
+ // Computes the runs_ array from the text run.
+ void FillRuns();
+
+ // Computes the shapes_ array given an runs_ array already filled in.
+ void FillShapes();
+
+ // Fills in the screen_order_ array (see below).
+ void FillScreenOrder();
+
+ // Called to update the glyph positions based on the current spacing options
+ // that are set.
+ void ApplySpacing();
+
+ // Normalizes all advances for spaces to the same width. This keeps windows
+ // from making spaces after Hindi characters larger, which is then
+ // inconsistent with our meaure of the width since WebKit doesn't include
+ // spaces in text-runs sent to uniscribe unless white-space:pre.
+ void AdjustSpaceAdvances();
+
+ // Returns the total width of a single item.
+ int AdvanceForItem(int item_index) const;
+
+ // Shapes a run (pointed to by |input|) using |hfont| first.
+ // Tries a series of fonts specified retrieved with NextWinFontData
+ // and finally a font covering characters in |*input|. A string pointed
+ // by |input| comes from ScriptItemize and is supposed to contain
+ // characters belonging to a single script aside from characters
+ // common to all scripts (e.g. space).
+ bool Shape(const wchar_t* input,
+ int item_length,
+ int num_glyphs,
+ SCRIPT_ITEM& run,
+ Shaping& shaping);
+
+ // Gets Windows font data for the next best font to try in the list
+ // of fonts. When there's no more font available, returns false
+ // without touching any of out params. Need to call ResetFontIndex
+ // to start scanning of the font list from the beginning.
+ virtual bool NextWinFontData(HFONT* hfont,
+ SCRIPT_CACHE** script_cache,
+ SCRIPT_FONTPROPERTIES** font_properties,
+ int* ascent) {
+ return false;
+ }
+
+ // Resets the font index to the first in the list of fonts
+ // to try after the primaryFont turns out not to work. With font_index
+ // reset, NextWinFontData scans fallback fonts from the beginning.
+ virtual void ResetFontIndex() {}
+
+ // The input data for this run of Uniscribe. See the constructor.
+ const wchar_t* input_;
+ const int input_length_;
+ const bool is_rtl_;
+
+ // Windows font data for the primary font :
+ // In a sense, logfont_ and style_ are redundant because
+ // hfont_ contains all the information. However, invoking GetObject,
+ // everytime we need the height and the style, is rather expensive so
+ // that we cache them. Would it be better to add getter and (virtual)
+ // setter for the height and the style of the primary font, instead of
+ // logfont_? Then, a derived class ctor can set ascent_, height_ and style_
+ // if they're known. Getters for them would have to 'infer' their values from
+ // hfont_ ONLY when they're not set.
+ HFONT hfont_;
+ SCRIPT_CACHE* script_cache_;
+ SCRIPT_FONTPROPERTIES* font_properties_;
+ int ascent_;
+ LOGFONT logfont_;
+ int style_;
+
+ // Options, see the getters/setters above.
+ bool directional_override_;
+ bool inhibit_ligate_;
+ int letter_spacing_;
+ int space_width_;
+ int word_spacing_;
+ int justification_width_;
+
+ // Uniscribe breaks the text into Runs. These are one length of text that is
+ // in one script and one direction. This array is in reading order.
+ StackVector<SCRIPT_ITEM, UNISCRIBE_STATE_STACK_RUNS> runs_;
+
+ StackVector<Shaping, UNISCRIBE_STATE_STACK_RUNS> shapes_;
+
+ // This is a mapping between reading order and screen order for the items.
+ // Uniscribe's items array are in reading order. For right-to-left text,
+ // or mixed (although WebKit's |TextRun| should really be only one
+ // direction), this makes it very difficult to compute character offsets
+ // and positions. This list is in screen order from left to right, and
+ // gives the index into the |runs_| and |shapes_| arrays of each
+ // subsequent item.
+ StackVector<int, UNISCRIBE_STATE_STACK_RUNS> screen_order_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(UniscribeState);
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_UNISCRIBE_H__
diff --git a/base/gfx/uniscribe_unittest.cc b/base/gfx/uniscribe_unittest.cc
new file mode 100644
index 0000000..8b2419c
--- /dev/null
+++ b/base/gfx/uniscribe_unittest.cc
@@ -0,0 +1,164 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/uniscribe.h"
+#include "base/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// This must be in the gfx namespace for the friend statements in uniscribe.h
+// to work.
+namespace gfx {
+
+namespace {
+
+class UniscribeTest : public testing::Test {
+ public:
+ UniscribeTest() {
+ }
+
+ // Returns an HFONT with the given name. The caller does not have to free
+ // this, it will be automatically freed at the end of the test. Returns NULL
+ // on failure. On success, the
+ HFONT MakeFont(const wchar_t* font_name, SCRIPT_CACHE** cache) {
+ LOGFONT lf;
+ memset(&lf, 0, sizeof(LOGFONT));
+ lf.lfHeight = 20;
+ wcscpy_s(lf.lfFaceName, font_name);
+
+ HFONT hfont = CreateFontIndirect(&lf);
+ if (!hfont)
+ return NULL;
+
+ *cache = new SCRIPT_CACHE;
+ **cache = NULL;
+ created_fonts_.push_back(std::make_pair(hfont, *cache));
+ return hfont;
+ }
+
+ protected:
+ // Default font properties structure for tests to use.
+ SCRIPT_FONTPROPERTIES properties_;
+
+ private:
+ virtual void SetUp() {
+ memset(&properties_, 0, sizeof(SCRIPT_FONTPROPERTIES));
+ properties_.cBytes = sizeof(SCRIPT_FONTPROPERTIES);
+ properties_.wgBlank = ' ';
+ properties_.wgDefault = '?'; // Used when the character is not in the font.
+ properties_.wgInvalid = '#'; // Used for invalid characters.
+ }
+
+ virtual void TearDown() {
+ // Free any allocated fonts.
+ for (size_t i = 0; i < created_fonts_.size(); i++) {
+ DeleteObject(created_fonts_[i].first);
+ ScriptFreeCache(created_fonts_[i].second);
+ delete created_fonts_[i].second;
+ }
+ created_fonts_.clear();
+ }
+
+ // Tracks allocated fonts so we can delete them at the end of the test.
+ // The script cache pointer is heap allocated and must be freed.
+ std::vector< std::pair<HFONT, SCRIPT_CACHE*> > created_fonts_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(UniscribeTest);
+};
+
+} // namespace
+
+// This test tests giving Uniscribe a very large buffer, which will cause a
+// failure.
+TEST_F(UniscribeTest, TooBig) {
+ // This test will only run on Windows XP. It seems Uniscribe does not have the
+ // internal limit on Windows 2000 that we rely on to cause this failure.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+
+ // Make a large string with an e with a zillion combining accents.
+ std::wstring input(L"e");
+ for (int i = 0; i < 100000; i++)
+ input.push_back(0x301); // Combining acute accent.
+
+ SCRIPT_CACHE* script_cache;
+ HFONT hfont = MakeFont(L"Times New Roman", &script_cache);
+ ASSERT_TRUE(hfont);
+
+ // Test a long string without the normal length protection we have. This will
+ // cause shaping to fail.
+ {
+ gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
+ false, hfont, script_cache, &properties_);
+ uniscribe.InitWithOptionalLengthProtection(false);
+
+ // There should be one shaping entry, with nothing in it.
+ ASSERT_EQ(1, uniscribe.shapes_->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].glyphs->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].logs->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].visattr->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].advance->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].offsets->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].justify->size());
+ EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcA);
+ EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcB);
+ EXPECT_EQ(0, uniscribe.shapes_[0].abc.abcC);
+
+ // The sizes of the other stuff should match the shaping entry.
+ EXPECT_EQ(1, uniscribe.runs_->size());
+ EXPECT_EQ(1, uniscribe.screen_order_->size());
+
+ // Check that the various querying functions handle the empty case properly.
+ EXPECT_EQ(0, uniscribe.Width());
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+ EXPECT_EQ(0, uniscribe.XToCharacter(0));
+ EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+ }
+
+ // Now test the very large string and make sure it is handled properly by the
+ // length protection.
+ {
+ gfx::UniscribeState uniscribe(input.data(), static_cast<int>(input.size()),
+ false, hfont, script_cache, &properties_);
+ uniscribe.InitWithOptionalLengthProtection(true);
+
+ // There should be 0 runs and shapes.
+ EXPECT_EQ(0, uniscribe.runs_->size());
+ EXPECT_EQ(0, uniscribe.shapes_->size());
+ EXPECT_EQ(0, uniscribe.screen_order_->size());
+
+ EXPECT_EQ(0, uniscribe.Width());
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(0));
+ EXPECT_EQ(0, uniscribe.FirstGlyphForCharacter(1000));
+ EXPECT_EQ(0, uniscribe.XToCharacter(0));
+ EXPECT_EQ(0, uniscribe.XToCharacter(1000));
+ }
+}
+
+} // namespace gfx \ No newline at end of file
diff --git a/base/gfx/vector_canvas.cc b/base/gfx/vector_canvas.cc
new file mode 100644
index 0000000..6f02f11
--- /dev/null
+++ b/base/gfx/vector_canvas.cc
@@ -0,0 +1,109 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_canvas.h"
+
+#include "base/gfx/vector_device.h"
+#include "base/logging.h"
+
+namespace gfx {
+
+VectorCanvas::VectorCanvas() {
+}
+
+VectorCanvas::VectorCanvas(HDC dc, int width, int height) {
+ initialize(dc, width, height);
+}
+
+VectorCanvas::~VectorCanvas() {
+}
+
+void VectorCanvas::initialize(HDC context, int width, int height) {
+ SkDevice* device = createPlatformDevice(width, height, true, context);
+ setDevice(device);
+ device->unref(); // was created with refcount 1, and setDevice also refs
+}
+
+SkBounder* VectorCanvas::setBounder(SkBounder* bounder) {
+ if (!IsTopDeviceVectorial())
+ return PlatformCanvas::setBounder(bounder);
+
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+ return NULL;
+}
+
+SkDevice* VectorCanvas::createDevice(SkBitmap::Config config,
+ int width, int height,
+ bool is_opaque, bool isForLayer) {
+ DCHECK(config == SkBitmap::kARGB_8888_Config);
+ return createPlatformDevice(width, height, is_opaque, NULL);
+}
+
+SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) {
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+ return NULL;
+}
+
+SkDevice* VectorCanvas::createPlatformDevice(int width,
+ int height, bool is_opaque,
+ HANDLE shared_section) {
+ if (!is_opaque) {
+ // TODO(maruel): http://b/1184002 1184002 When restoring a semi-transparent
+ // layer, i.e. merging it, we need to rasterize it because GDI doesn't
+ // support transparency except for AlphaBlend(). Right now, a
+ // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
+ // call is being done. The way to save a layer would be to create an
+ // EMF-based VectorDevice and have this device registers the drawing. When
+ // playing back the device into a bitmap, do it at the printer's dpi instead
+ // of the layout's dpi (which is much lower).
+ return PlatformCanvas::createPlatformDevice(width, height, is_opaque,
+ shared_section);
+ }
+
+ // TODO(maruel): http://b/1183870 Look if it would be worth to increase the
+ // resolution by ~10x (any worthy factor) to increase the rendering precision
+ // (think about printing) while using a relatively low dpi. This happens
+ // because we receive float as input but the GDI functions works with
+ // integers. The idea is to premultiply the matrix with this factor and
+ // multiply each SkScalar that are passed to SkScalarRound(value) as
+ // SkScalarRound(value * 10). Safari is already doing the same for text
+ // rendering.
+ DCHECK(shared_section);
+ PlatformDevice* device = VectorDevice::create(
+ reinterpret_cast<HDC>(shared_section), width, height);
+ return device;
+}
+
+bool VectorCanvas::IsTopDeviceVectorial() const {
+ return getTopPlatformDevice().IsVectorial();
+}
+
+} // namespace gfx
diff --git a/base/gfx/vector_canvas.h b/base/gfx/vector_canvas.h
new file mode 100644
index 0000000..d6a5703
--- /dev/null
+++ b/base/gfx/vector_canvas.h
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_VECTOR_CANVAS_H__
+#define BASE_GFX_VECTOR_CANVAS_H__
+
+#include "base/gfx/platform_canvas.h"
+#include "base/gfx/vector_device.h"
+
+namespace gfx {
+
+// This class is a specialization of the regular PlatformCanvas. It is designed
+// to work with a VectorDevice to manage platform-specific drawing. It allows
+// using both Skia operations and platform-specific operations. It *doesn't*
+// support reading back from the bitmap backstore since it is not used.
+class VectorCanvas : public PlatformCanvas {
+ public:
+ VectorCanvas();
+ VectorCanvas(HDC dc, int width, int height);
+ virtual ~VectorCanvas();
+
+ // For two-part init, call if you use the no-argument constructor above
+ void initialize(HDC context, int width, int height);
+
+ virtual SkBounder* setBounder(SkBounder*);
+ virtual SkDevice* createDevice(SkBitmap::Config config,
+ int width, int height,
+ bool is_opaque, bool isForLayer);
+ virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter);
+
+ private:
+ // |is_opaque| is unused. |shared_section| is in fact the HDC used for output.
+ virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque,
+ HANDLE shared_section);
+
+ // Returns true if the top device is vector based and not bitmap based.
+ bool IsTopDeviceVectorial() const;
+
+ DISALLOW_EVIL_CONSTRUCTORS(VectorCanvas);
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_VECTOR_CANVAS_H__
diff --git a/base/gfx/vector_canvas_unittest.cc b/base/gfx/vector_canvas_unittest.cc
new file mode 100644
index 0000000..57ff34d
--- /dev/null
+++ b/base/gfx/vector_canvas_unittest.cc
@@ -0,0 +1,1032 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_canvas.h"
+
+#include <vector>
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/png_decoder.h"
+#include "base/gfx/png_encoder.h"
+#include "base/gfx/size.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "SkDashPathEffect.h"
+
+namespace {
+
+const wchar_t* const kGenerateSwitch = L"vector-canvas-generate";
+
+// Base class for unit test that uses data. It initializes a directory path
+// based on the test's name.
+class DataUnitTest : public testing::Test {
+ public:
+ DataUnitTest(const std::wstring& base_path) : base_path_(base_path) { }
+
+ protected:
+ // Load the test's data path.
+ virtual void SetUp() {
+ const testing::TestInfo& test_info =
+ *testing::UnitTest::GetInstance()->current_test_info();
+ PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
+ file_util::AppendToPath(&test_dir_, base_path_);
+ file_util::AppendToPath(&test_dir_, L"data");
+ file_util::AppendToPath(&test_dir_,
+ ASCIIToWide(test_info.test_case_name()));
+ file_util::AppendToPath(&test_dir_, ASCIIToWide(test_info.name()));
+
+ // Hack for a quick lowercase. We assume all the tests names are ASCII.
+ std::string tmp(WideToASCII(test_dir_));
+ for (size_t i = 0; i < tmp.size(); ++i)
+ tmp[i] = ToLowerASCII(tmp[i]);
+ test_dir_ = ASCIIToWide(tmp);
+ }
+
+ // Returns the fully qualified path of directory containing test data files.
+ const std::wstring& test_dir() const {
+ return test_dir_;
+ }
+
+ // Returns the fully qualified path of a data file.
+ std::wstring test_file(const std::wstring& filename) const {
+ // Hack for a quick lowercase. We assume all the test data file names are
+ // ASCII.
+ std::string tmp(WideToASCII(filename));
+ for (size_t i = 0; i < tmp.size(); ++i)
+ tmp[i] = ToLowerASCII(tmp[i]);
+
+ std::wstring path(test_dir());
+ file_util::AppendToPath(&path, ASCIIToWide(tmp));
+ return path;
+ }
+
+ private:
+ // Path where the unit test is coming from: base, net, chrome, etc.
+ std::wstring base_path_;
+
+ // Path to directory used to contain the test data.
+ std::wstring test_dir_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(DataUnitTest);
+};
+
+// Lightweight HDC management.
+class Context {
+ public:
+ Context() : context_(CreateCompatibleDC(NULL)) {
+ EXPECT_TRUE(context_);
+ }
+ ~Context() {
+ DeleteDC(context_);
+ }
+
+ HDC context() const { return context_; }
+
+ private:
+ HDC context_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Context);
+};
+
+// Lightweight HBITMAP management.
+class Bitmap {
+ public:
+ Bitmap(const Context& context, int x, int y) {
+ BITMAPINFOHEADER hdr;
+ gfx::CreateBitmapHeader(x, y, &hdr);
+ bitmap_ = CreateDIBSection(context.context(),
+ reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+ &data_, NULL, 0);
+ EXPECT_TRUE(bitmap_);
+ EXPECT_TRUE(SelectObject(context.context(), bitmap_));
+ }
+ ~Bitmap() {
+ EXPECT_TRUE(DeleteObject(bitmap_));
+ }
+
+ private:
+ HBITMAP bitmap_;
+
+ void* data_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Bitmap);
+};
+
+// Lightweight raw-bitmap management. The image, once initialized, is immuable.
+// It is mainly used for comparison.
+class Image {
+ public:
+ // Creates the image from the given filename on disk.
+ Image(const std::wstring& filename) : ignore_alpha_(true) {
+ std::string compressed;
+ file_util::ReadFileToString(filename, &compressed);
+ EXPECT_TRUE(compressed.size());
+
+ int w;
+ int h;
+ EXPECT_TRUE(PNGDecoder::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.c_str()),
+ compressed.size(), PNGDecoder::FORMAT_BGRA, &data_, &w, &h));
+ size_.SetSize(w, h);
+ row_length_ = w * sizeof(uint32);
+ }
+
+ // Loads the image from a canvas.
+ Image(const gfx::PlatformCanvas& canvas) : ignore_alpha_(true) {
+ // Use a different way to access the bitmap. The normal way would be to
+ // query the SkBitmap.
+ HDC context = canvas.getTopPlatformDevice().getBitmapDC();
+ HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
+ EXPECT_TRUE(bitmap != NULL);
+ // Initialize the clip region to the entire bitmap.
+ BITMAP bitmap_data;
+ EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data),
+ sizeof(BITMAP));
+ size_.SetSize(bitmap_data.bmWidth, bitmap_data.bmHeight);
+ row_length_ = bitmap_data.bmWidthBytes;
+ size_t size = row_length_ * size_.height();
+ data_.resize(size);
+ memcpy(&*data_.begin(), bitmap_data.bmBits, size);
+ }
+
+ // Loads the image from a canvas.
+ Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
+ SkAutoLockPixels lock(bitmap);
+ size_.SetSize(bitmap.width(), bitmap.height());
+ row_length_ = static_cast<int>(bitmap.rowBytes());
+ size_t size = row_length_ * size_.height();
+ data_.resize(size);
+ memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
+ }
+
+ const gfx::Size& size() const {
+ return size_;
+ }
+
+ int row_length() const {
+ return row_length_;
+ }
+
+ // Save the image to a png file. Used to create the initial test files.
+ void SaveToFile(const std::wstring& filename) {
+ std::vector<unsigned char> compressed;
+ ASSERT_TRUE(PNGEncoder::Encode(&*data_.begin(),
+ PNGEncoder::FORMAT_BGRA,
+ size_.width(),
+ size_.height(),
+ row_length_,
+ true,
+ &compressed));
+ ASSERT_TRUE(compressed.size());
+ FILE* f;
+ ASSERT_EQ(_wfopen_s(&f, filename.c_str(), L"wbS"), 0);
+ ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
+ compressed.size());
+ fclose(f);
+ }
+
+ // Returns the percentage of the image that is different from the other,
+ // between 0 and 100.
+ double PercentageDifferent(const Image& rhs) const {
+ if (size_ != rhs.size_ || row_length_ != rhs.row_length_ ||
+ size_.width() == 0 || size_.height() == 0)
+ return 100.; // When of different size or empty, they are 100% different.
+
+ // Compute pixels different in the overlap
+ int pixels_different = 0;
+ for (int y = 0; y < size_.height(); ++y) {
+ for (int x = 0; x < size_.width(); ++x) {
+ uint32_t lhs_pixel = pixel_at(x, y);
+ uint32_t rhs_pixel = rhs.pixel_at(x, y);
+ if (lhs_pixel != rhs_pixel)
+ ++pixels_different;
+ }
+ }
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ double total_pixels = static_cast<double>(size_.width()) *
+ static_cast<double>(size_.height());
+ return static_cast<double>(pixels_different) / total_pixels * 100.;
+ }
+
+ // Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
+ // depending on ignore_alpha_.
+ uint32 pixel_at(int x, int y) const {
+ EXPECT_TRUE(x >= 0 && x < size_.width());
+ EXPECT_TRUE(y >= 0 && y < size_.height());
+ const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
+ const uint32* data_row = data + y * row_length_ / sizeof(uint32);
+ if (ignore_alpha_)
+ return data_row[x] & 0xFFFFFF; // Strip out A.
+ else
+ return data_row[x];
+ }
+
+ private:
+ // Pixel dimensions of the image.
+ gfx::Size size_;
+
+ // Length of a line in bytes.
+ int row_length_;
+
+ // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
+ // 0xABGR).
+ std::vector<unsigned char> data_;
+
+ // Flag to signal if the comparison functions should ignore the alpha channel.
+ const bool ignore_alpha_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Image);
+};
+
+// Base for tests. Capability to process an image.
+class ImageTest : public DataUnitTest {
+ public:
+ typedef DataUnitTest parent;
+
+ // In what state is the test running.
+ enum ProcessAction {
+ GENERATE,
+ COMPARE,
+ NOOP,
+ };
+
+ ImageTest(const std::wstring& base_path, ProcessAction default_action)
+ : parent(base_path),
+ action_(default_action) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ parent::SetUp();
+
+ if (action_ == GENERATE) {
+ // Make sure the directory exist.
+ file_util::CreateDirectory(test_dir());
+ }
+ }
+
+ // Compares or saves the bitmap currently loaded in the context, depending on
+ // kGenerating value. Returns 0 on success or any positive value between ]0,
+ // 100] on failure. The return value is the percentage of difference between
+ // the image in the file and the image in the canvas.
+ double ProcessCanvas(const gfx::PlatformCanvas& canvas,
+ std::wstring filename) const {
+ filename += L".png";
+ switch (action_) {
+ case GENERATE:
+ SaveImage(canvas, filename);
+ return 0.;
+ case COMPARE:
+ return CompareImage(canvas, filename);
+ case NOOP:
+ return 0;
+ default:
+ // Invalid state, returns that the image is 100 different.
+ return 100.;
+ }
+ }
+
+ // Compares the bitmap currently loaded in the context with the file. Returns
+ // the percentage of pixel difference between both images, between 0 and 100.
+ double CompareImage(const gfx::PlatformCanvas& canvas,
+ const std::wstring& filename) const {
+ Image image1(canvas);
+ Image image2(test_file(filename));
+ double diff = image1.PercentageDifferent(image2);
+ return diff;
+ }
+
+ // Saves the bitmap currently loaded in the context into the file.
+ void SaveImage(const gfx::PlatformCanvas& canvas,
+ const std::wstring& filename) const {
+ Image(canvas).SaveToFile(test_file(filename));
+ }
+
+ ProcessAction action_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ImageTest);
+};
+
+// Premultiply the Alpha channel on the R, B and G channels.
+void Premultiply(SkBitmap bitmap) {
+ SkAutoLockPixels lock(bitmap);
+ for (int x = 0; x < bitmap.width(); ++x) {
+ for (int y = 0; y < bitmap.height(); ++y) {
+ uint32_t* pixel_addr = bitmap.getAddr32(x, y);
+ uint32_t color = *pixel_addr;
+ BYTE alpha = SkColorGetA(color);
+ if (!alpha) {
+ *pixel_addr = 0;
+ } else {
+ BYTE alpha_offset = alpha / 2;
+ *pixel_addr = SkColorSetARGB(
+ SkColorGetA(color),
+ (SkColorGetR(color) * 255 + alpha_offset) / alpha,
+ (SkColorGetG(color) * 255 + alpha_offset) / alpha,
+ (SkColorGetB(color) * 255 + alpha_offset) / alpha);
+ }
+ }
+ }
+}
+
+void LoadPngFileToSkBitmap(const std::wstring& file, SkBitmap* bitmap) {
+ std::string compressed;
+ file_util::ReadFileToString(file, &compressed);
+ EXPECT_TRUE(compressed.size());
+ // Extra-lame. If you care, fix it.
+ std::vector<unsigned char> data;
+ data.assign(reinterpret_cast<const unsigned char*>(compressed.c_str()),
+ reinterpret_cast<const unsigned char*>(compressed.c_str() +
+ compressed.size()));
+ EXPECT_TRUE(PNGDecoder::Decode(&data, bitmap));
+ EXPECT_FALSE(bitmap->isOpaque());
+ Premultiply(*bitmap);
+}
+
+} // namespace
+
+// Streams an image.
+inline std::ostream& operator<<(std::ostream& out, const Image& image) {
+ return out << "Image(" << image.size() << ", " << image.row_length() << ")";
+}
+
+// Runs simultaneously the same drawing commands on VectorCanvas and
+// PlatformCanvas and compare the results.
+class VectorCanvasTest : public ImageTest {
+ public:
+ typedef ImageTest parent;
+
+ VectorCanvasTest() : parent(L"base", CurrentMode()), compare_canvas_(true) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ parent::SetUp();
+ Init(100);
+ number_ = 0;
+ }
+
+ virtual void TearDown() {
+ delete pcanvas_;
+ pcanvas_ = NULL;
+
+ delete vcanvas_;
+ vcanvas_ = NULL;
+
+ delete bitmap_;
+ bitmap_ = NULL;
+
+ delete context_;
+ context_ = NULL;
+
+ parent::TearDown();
+ }
+
+ void Init(int size) {
+ size_ = size;
+ context_ = new Context();
+ bitmap_ = new Bitmap(*context_, size_, size_);
+ vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_);
+ pcanvas_ = new gfx::PlatformCanvas(size_, size_, false);
+
+ // Clear white.
+ vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+ pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+ }
+
+ // Compares both canvas and returns the pixel difference in percentage between
+ // both images. 0 on success and ]0, 100] on failure.
+ double ProcessImage(const std::wstring& filename) {
+ std::wstring number(StringPrintf(L"%02d_", number_++));
+ double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
+ double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
+ if (!compare_canvas_)
+ return std::max(diff1, diff2);
+
+ Image image1(*vcanvas_);
+ Image image2(*pcanvas_);
+ double diff = image1.PercentageDifferent(image2);
+ return std::max(std::max(diff1, diff2), diff);
+ }
+
+ // Returns COMPARE, which is the default. If kGenerateSwitch command
+ // line argument is used to start this process, GENERATE is returned instead.
+ static ProcessAction CurrentMode() {
+ return CommandLine().HasSwitch(kGenerateSwitch) ? GENERATE : COMPARE;
+ }
+
+ // Length in x and y of the square canvas.
+ int size_;
+
+ // Current image number in the current test. Used to number of test files.
+ int number_;
+
+ // A temporary HDC to draw into.
+ Context* context_;
+
+ // Bitmap created inside context_.
+ Bitmap* bitmap_;
+
+ // Vector based canvas.
+ gfx::VectorCanvas* vcanvas_;
+
+ // Pixel based canvas.
+ gfx::PlatformCanvas* pcanvas_;
+
+ // When true (default), vcanvas_ and pcanvas_ contents are compared and
+ // verified to be identical.
+ bool compare_canvas_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Actual tests
+
+TEST_F(VectorCanvasTest, Uninitialized) {
+ // Do a little mubadumba do get uninitialized stuff.
+ VectorCanvasTest::TearDown();
+
+ // The goal is not to verify that have the same uninitialized data.
+ compare_canvas_ = false;
+
+ context_ = new Context();
+ bitmap_ = new Bitmap(*context_, size_, size_);
+ vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_);
+ pcanvas_ = new gfx::PlatformCanvas(size_, size_, false);
+
+ // VectorCanvas default initialization is black.
+ // PlatformCanvas default initialization is almost white 0x01FFFEFD (invalid
+ // Skia color) in both Debug and Release. See magicTransparencyColor in
+ // platform_device.cc
+ EXPECT_EQ(0., ProcessImage(L"empty"));
+}
+
+TEST_F(VectorCanvasTest, BasicDrawing) {
+ EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
+ << L"clean";
+ EXPECT_EQ(0., ProcessImage(L"clean"));
+
+ // Clear white.
+ {
+ vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+ pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawARGB"));
+
+ // Diagonal line top-left to bottom-right.
+ {
+ SkPaint paint;
+ // Default color is black.
+ vcanvas_->drawLine(10, 10, 90, 90, paint);
+ pcanvas_->drawLine(10, 10, 90, 90, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawLine_black"));
+
+ // Rect.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorGREEN);
+ vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+ pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawRect_green"));
+
+ // A single-point rect doesn't leave any mark.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+ pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawRect_noop"));
+
+ // Rect.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+ pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawRect_noop"));
+
+ // Empty again
+ {
+ vcanvas_->drawPaint(SkPaint());
+ pcanvas_->drawPaint(SkPaint());
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawPaint_black"));
+
+ // Horizontal line left to right.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(10, 20, 90, 20, paint);
+ pcanvas_->drawLine(10, 20, 90, 20, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawLine_left_to_right"));
+
+ // Vertical line downward.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(30, 10, 30, 90, paint);
+ pcanvas_->drawLine(30, 10, 30, 90, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawLine_red"));
+}
+
+TEST_F(VectorCanvasTest, Circles) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Stroked Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 75, 10);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorMAGENTA);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"circle_stroke"));
+
+ // Filled Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 25, 10);
+ paint.setStyle(SkPaint::kFill_Style);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"circle_fill"));
+
+ // Stroked Circle over.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 25, 10);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"circle_over_strike"));
+
+ // Stroke and Fill Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(12, 50, 10);
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"circle_stroke_and_fill"));
+
+ // Line + Quad + Cubic.
+ {
+ SkPaint paint;
+ SkPath path;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorGREEN);
+ path.moveTo(1, 1);
+ path.lineTo(60, 40);
+ path.lineTo(80, 80);
+ path.quadTo(20, 50, 10, 90);
+ path.quadTo(50, 20, 90, 10);
+ path.cubicTo(20, 40, 50, 50, 10, 10);
+ path.cubicTo(30, 20, 50, 50, 90, 10);
+ path.addRect(90, 90, 95, 96);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"mixed_stroke"));
+}
+
+TEST_F(VectorCanvasTest, LineOrientation) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ // Left to right.
+ vcanvas_->drawLine(10, 20, 90, 20, paint);
+ pcanvas_->drawLine(10, 20, 90, 20, paint);
+ // Right to left.
+ vcanvas_->drawLine(90, 30, 10, 30, paint);
+ pcanvas_->drawLine(90, 30, 10, 30, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"horizontal"));
+
+ // Vertical lines.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ // Top down.
+ vcanvas_->drawLine(20, 10, 20, 90, paint);
+ pcanvas_->drawLine(20, 10, 20, 90, paint);
+ // Bottom up.
+ vcanvas_->drawLine(30, 90, 30, 10, paint);
+ pcanvas_->drawLine(30, 90, 30, 10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"vertical"));
+
+ // Try again with a 180 degres rotation.
+ vcanvas_->rotate(180);
+ pcanvas_->rotate(180);
+
+ // Horizontal lines (rotated).
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(-10, -25, -90, -25, paint);
+ pcanvas_->drawLine(-10, -25, -90, -25, paint);
+ vcanvas_->drawLine(-90, -35, -10, -35, paint);
+ pcanvas_->drawLine(-90, -35, -10, -35, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"horizontal_180"));
+
+ // Vertical lines (rotated).
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(-25, -10, -25, -90, paint);
+ pcanvas_->drawLine(-25, -10, -25, -90, paint);
+ vcanvas_->drawLine(-35, -90, -35, -10, paint);
+ pcanvas_->drawLine(-35, -90, -35, -10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"vertical_180"));
+}
+
+TEST_F(VectorCanvasTest, PathOrientation) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorRED);
+ SkPath path;
+ SkPoint start;
+ start.set(10, 20);
+ SkPoint end;
+ end.set(90, 20);
+ path.moveTo(start);
+ path.lineTo(end);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawPath_ltr"));
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorRED);
+ SkPath path;
+ SkPoint start;
+ start.set(90, 30);
+ SkPoint end;
+ end.set(10, 30);
+ path.moveTo(start);
+ path.lineTo(end);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"drawPath_rtl"));
+}
+
+TEST_F(VectorCanvasTest, DiagonalLines) {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+
+ vcanvas_->drawLine(10, 10, 90, 90, paint);
+ pcanvas_->drawLine(10, 10, 90, 90, paint);
+ EXPECT_EQ(0., ProcessImage(L"nw-se"));
+
+ // Starting here, there is NO WAY to make them agree. At least verify that the
+ // output doesn't change across versions. This test is disabled. See bug
+ // 1060231.
+ compare_canvas_ = false;
+
+ vcanvas_->drawLine(10, 95, 90, 15, paint);
+ pcanvas_->drawLine(10, 95, 90, 15, paint);
+ EXPECT_EQ(0., ProcessImage(L"sw-ne"));
+
+ vcanvas_->drawLine(90, 10, 10, 90, paint);
+ pcanvas_->drawLine(90, 10, 10, 90, paint);
+ EXPECT_EQ(0., ProcessImage(L"ne-sw"));
+
+ vcanvas_->drawLine(95, 90, 15, 10, paint);
+ pcanvas_->drawLine(95, 90, 15, 10, paint);
+ EXPECT_EQ(0., ProcessImage(L"se-nw"));
+}
+
+TEST_F(VectorCanvasTest, PathEffects) {
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 1, 1 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ vcanvas_->drawLine(10, 10, 90, 10, paint);
+ pcanvas_->drawLine(10, 10, 90, 10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"dash_line"));
+
+
+ // Starting here, there is NO WAY to make them agree. At least verify that the
+ // output doesn't change across versions. This test is disabled. See bug
+ // 1060231.
+ compare_canvas_ = false;
+
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 3, 5 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ path.moveTo(10, 15);
+ path.lineTo(90, 15);
+ path.lineTo(90, 90);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"dash_path"));
+
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 2, 1 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+ pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(L"dash_rect"));
+
+ // This thing looks like it has been drawn by a 3 years old kid. I haven't
+ // filed a bug on this since I guess nobody is expecting this to look nice.
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 1, 1 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ path.addCircle(50, 75, 10);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ EXPECT_EQ(0., ProcessImage(L"circle"));
+ }
+}
+
+TEST_F(VectorCanvasTest, Bitmaps) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap);
+ vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"opaque"));
+ }
+
+ {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap);
+ vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+ pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+ EXPECT_EQ(0., ProcessImage(L"alpha"));
+ }
+}
+
+TEST_F(VectorCanvasTest, ClippingRect) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+
+ vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"rect"));
+}
+
+TEST_F(VectorCanvasTest, ClippingPath) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+ SkPath path;
+ path.addCircle(20, 20, 10);
+ vcanvas_->clipPath(path);
+ pcanvas_->clipPath(path);
+
+ vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"path"));
+}
+
+TEST_F(VectorCanvasTest, ClippingCombined) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+ SkPath path;
+ path.addCircle(20, 20, 10);
+ vcanvas_->clipPath(path, SkRegion::kUnion_Op);
+ pcanvas_->clipPath(path, SkRegion::kUnion_Op);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"combined"));
+}
+
+TEST_F(VectorCanvasTest, ClippingIntersect) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+ SkPath path;
+ path.addCircle(23, 23, 15);
+ vcanvas_->clipPath(path);
+ pcanvas_->clipPath(path);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"intersect"));
+}
+
+TEST_F(VectorCanvasTest, ClippingClean) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+ {
+ SkRegion old_region(pcanvas_->getTotalClip());
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"clipped"));
+ vcanvas_->clipRegion(old_region, SkRegion::kReplace_Op);
+ pcanvas_->clipRegion(old_region, SkRegion::kReplace_Op);
+ }
+ {
+ // Verify that the clipping region has been fixed back.
+ vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(L"unclipped"));
+ }
+}
+
+TEST_F(VectorCanvasTest, Matrix) {
+ // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests
+ // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't
+ // really care about Windows 2000 pixel colors.
+ if (win_util::GetWinVersion() <= win_util::WINVERSION_2000)
+ return;
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap);
+ {
+ vcanvas_->translate(15, 3);
+ pcanvas_->translate(15, 3);
+ vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ EXPECT_EQ(0., ProcessImage(L"translate1"));
+ }
+ {
+ vcanvas_->translate(-30, -23);
+ pcanvas_->translate(-30, -23);
+ vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ EXPECT_EQ(0., ProcessImage(L"translate2"));
+ }
+ vcanvas_->resetMatrix();
+ pcanvas_->resetMatrix();
+
+ // For scaling and rotation, they use a different algorithm (nearest
+ // neighborhood vs smoothing). At least verify that the output doesn't change
+ // across versions.
+ compare_canvas_ = false;
+
+ {
+ vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+ pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+ vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+ pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+ EXPECT_EQ(0., ProcessImage(L"scale"));
+ }
+ vcanvas_->resetMatrix();
+ pcanvas_->resetMatrix();
+
+ {
+ vcanvas_->rotate(67);
+ pcanvas_->rotate(67);
+ vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+ pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+ EXPECT_EQ(0., ProcessImage(L"rotate"));
+ }
+}
diff --git a/base/gfx/vector_device.cc b/base/gfx/vector_device.cc
new file mode 100644
index 0000000..c7e20f6
--- /dev/null
+++ b/base/gfx/vector_device.cc
@@ -0,0 +1,646 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/gfx/vector_device.h"
+
+#include "base/gfx/bitmap_header.h"
+#include "base/gfx/skia_utils.h"
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+
+#include "SkUtils.h"
+
+namespace gfx {
+
+VectorDevice* VectorDevice::create(HDC dc, int width, int height) {
+ InitializeDC(dc);
+
+ // Link the SkBitmap to the current selected bitmap in the device context.
+ SkBitmap bitmap;
+ HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
+ bool succeeded = false;
+ if (selected_bitmap != NULL) {
+ BITMAP bitmap_data;
+ if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
+ sizeof(BITMAP)) {
+ // The context has a bitmap attached. Attach our SkBitmap to it.
+ // Warning: If the bitmap gets unselected from the HDC, VectorDevice has
+ // no way to detect this, so the HBITMAP could be released while SkBitmap
+ // still has a reference to it. Be cautious.
+ if (width == bitmap_data.bmWidth &&
+ height == bitmap_data.bmHeight) {
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ bitmap_data.bmWidth,
+ bitmap_data.bmHeight,
+ bitmap_data.bmWidthBytes);
+ bitmap.setPixels(bitmap_data.bmBits);
+ succeeded = true;
+ }
+ }
+ }
+
+ if (!succeeded)
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+
+ return new VectorDevice(dc, bitmap);
+}
+
+VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap)
+ : PlatformDevice(bitmap),
+ hdc_(dc),
+ previous_brush_(NULL),
+ previous_pen_(NULL),
+ offset_x_(0),
+ offset_y_(0) {
+ transform_.reset();
+}
+
+VectorDevice::~VectorDevice() {
+ DCHECK(previous_brush_ == NULL);
+ DCHECK(previous_pen_ == NULL);
+}
+
+
+void VectorDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ // TODO(maruel): Bypass the current transformation matrix.
+ SkRect rect;
+ rect.fLeft = 0;
+ rect.fTop = 0;
+ rect.fRight = SkIntToScalar(width() + 1);
+ rect.fBottom = SkIntToScalar(height() + 1);
+ drawRect(draw, rect, paint);
+}
+
+void VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+ size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ if (!count)
+ return;
+
+ if (mode == SkCanvas::kPoints_PointMode) {
+ NOTREACHED();
+ return;
+ }
+
+ SkPaint tmp_paint(paint);
+ tmp_paint.setStyle(SkPaint::kStroke_Style);
+
+ // Draw a path instead.
+ SkPath path;
+ switch (mode) {
+ case SkCanvas::kLines_PointMode:
+ if (count % 2) {
+ NOTREACHED();
+ return;
+ }
+ for (size_t i = 0; i < count / 2; ++i) {
+ path.moveTo(pts[2 * i]);
+ path.lineTo(pts[2 * i + 1]);
+ }
+ break;
+ case SkCanvas::kPolygon_PointMode:
+ path.moveTo(pts[0]);
+ for (size_t i = 1; i < count; ++i) {
+ path.lineTo(pts[i]);
+ }
+ break;
+ default:
+ NOTREACHED();
+ return;
+ }
+ // Draw the calculated path.
+ drawPath(draw, path, tmp_paint);
+}
+
+void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect,
+ const SkPaint& paint) {
+ if (paint.getPathEffect()) {
+ // Draw a path instead.
+ SkPath path_orginal;
+ path_orginal.addRect(rect);
+
+ // Apply the path effect to the rect.
+ SkPath path_modified;
+ paint.getFillPath(path_orginal, &path_modified);
+
+ // Removes the path effect from the temporary SkPaint object.
+ SkPaint paint_no_effet(paint);
+ paint_no_effet.setPathEffect(NULL)->safeUnref();
+
+ // Draw the calculated path.
+ drawPath(draw, path_modified, paint_no_effet);
+ return;
+ }
+
+ if (!ApplyPaint(paint)) {
+ return;
+ }
+ HDC dc = getBitmapDC();
+ if (!Rectangle(dc, SkScalarRound(rect.fLeft),
+ SkScalarRound(rect.fTop),
+ SkScalarRound(rect.fRight),
+ SkScalarRound(rect.fBottom))) {
+ NOTREACHED();
+ }
+ Cleanup();
+}
+
+void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint) {
+ if (paint.getPathEffect()) {
+ // Apply the path effect forehand.
+ SkPath path_modified;
+ paint.getFillPath(path, &path_modified);
+
+ // Removes the path effect from the temporary SkPaint object.
+ SkPaint paint_no_effet(paint);
+ paint_no_effet.setPathEffect(NULL)->safeUnref();
+
+ // Draw the calculated path.
+ drawPath(draw, path_modified, paint_no_effet);
+ return;
+ }
+
+ if (!ApplyPaint(paint)) {
+ return;
+ }
+ HDC dc = getBitmapDC();
+ PlatformDevice::LoadPathToDC(dc, path);
+ switch (paint.getStyle()) {
+ case SkPaint::kFill_Style: {
+ BOOL res = StrokeAndFillPath(dc);
+ DCHECK(res != 0);
+ break;
+ }
+ case SkPaint::kStroke_Style: {
+ BOOL res = StrokePath(dc);
+ DCHECK(res != 0);
+ break;
+ }
+ case SkPaint::kStrokeAndFill_Style: {
+ BOOL res = StrokeAndFillPath(dc);
+ DCHECK(res != 0);
+ break;
+ }
+ default:
+ NOTREACHED();
+ break;
+ }
+ Cleanup();
+}
+
+void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint) {
+ // Load the temporary matrix. This is what will translate, rotate and resize
+ // the bitmap.
+ SkMatrix actual_transform(transform_);
+ actual_transform.preConcat(matrix);
+ LoadTransformToDC(hdc_, actual_transform);
+
+ InternalDrawBitmap(bitmap, 0, 0, paint);
+
+ // Restore the original matrix.
+ LoadTransformToDC(hdc_, transform_);
+}
+
+void VectorDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) {
+ SkMatrix identity;
+ identity.reset();
+ LoadTransformToDC(hdc_, identity);
+
+ InternalDrawBitmap(bitmap, x, y, paint);
+
+ // Restore the original matrix.
+ LoadTransformToDC(hdc_, transform_);
+}
+
+void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+}
+
+void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+}
+
+void VectorDevice::drawTextOnPath(const SkDraw& draw, const void* text,
+ size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+}
+
+void VectorDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
+ int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ NOTREACHED();
+}
+
+void VectorDevice::drawDevice(const SkDraw& draw, SkDevice* device, int x,
+ int y, const SkPaint& paint) {
+ // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
+ // it is a vectorial device.
+ drawSprite(draw, device->accessBitmap(false), x, y, paint);
+}
+
+bool VectorDevice::ApplyPaint(const SkPaint& paint) {
+ // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
+ // This function does not execute the SkPaint drawing commands. These should
+ // be executed in drawPaint().
+
+ SkPaint::Style style = paint.getStyle();
+ if (!paint.getAlpha())
+ style = SkPaint::kStyleCount;
+
+ switch (style) {
+ case SkPaint::kFill_Style:
+ if (!CreateBrush(true, paint) ||
+ !CreatePen(false, paint))
+ return false;
+ break;
+ case SkPaint::kStroke_Style:
+ if (!CreateBrush(false, paint) ||
+ !CreatePen(true, paint))
+ return false;
+ break;
+ case SkPaint::kStrokeAndFill_Style:
+ if (!CreateBrush(true, paint) ||
+ !CreatePen(true, paint))
+ return false;
+ break;
+ default:
+ if (!CreateBrush(false, paint) ||
+ !CreatePen(false, paint))
+ return false;
+ break;
+ }
+
+ /*
+ getFlags();
+ isAntiAlias();
+ isDither()
+ isLinearText()
+ isSubpixelText()
+ isUnderlineText()
+ isStrikeThruText()
+ isFakeBoldText()
+ isDevKernText()
+ isFilterBitmap()
+
+ // Skia's text is not used. This should be fixed.
+ getTextAlign()
+ getTextScaleX()
+ getTextSkewX()
+ getTextEncoding()
+ getFontMetrics()
+ getFontSpacing()
+ */
+
+ // BUG 1094907: Implement shaders. Shaders currently in use:
+ // SkShader::CreateBitmapShader
+ // SkGradientShader::CreateRadial
+ // SkGradientShader::CreateLinear
+ // DCHECK(!paint.getShader());
+
+ // http://b/1106647 Implement loopers and mask filter. Looper currently in
+ // use:
+ // SkBlurDrawLooper is used for shadows.
+ // DCHECK(!paint.getLooper());
+ // DCHECK(!paint.getMaskFilter());
+
+ // http://b/1165900 Implement xfermode.
+ // DCHECK(!paint.getXfermode());
+
+ // The path effect should be processed before arriving here.
+ DCHECK(!paint.getPathEffect());
+
+ // These aren't used in the code. Verify this assumption.
+ DCHECK(!paint.getColorFilter());
+ DCHECK(!paint.getRasterizer());
+ // Reuse code to load Win32 Fonts.
+ DCHECK(!paint.getTypeface());
+ return true;
+}
+
+void VectorDevice::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region) {
+ transform_ = transform;
+ LoadTransformToDC(hdc_, transform_);
+ clip_region_ = region;
+ if (!clip_region_.isEmpty())
+ LoadClipRegion();
+}
+
+void VectorDevice::setDeviceOffset(int x, int y) {
+ offset_x_ = x;
+ offset_y_ = y;
+}
+
+void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) {
+ NOTREACHED();
+}
+
+void VectorDevice::LoadClipRegion() {
+ // We don't use transform_ for the clipping region since the translation is
+ // already applied to offset_x_ and offset_y_.
+ SkMatrix t;
+ t.reset();
+ t.postTranslate(SkIntToScalar(-offset_x_), SkIntToScalar(-offset_y_));
+ LoadClippingRegionToDC(hdc_, clip_region_, t);
+}
+
+bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) {
+ DCHECK(previous_brush_ == NULL);
+ // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
+ // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
+ // WHITE_BRUSH instead.
+
+ if (!use_brush) {
+ // Set the transparency.
+ if (0 == SetBkMode(hdc_, TRANSPARENT)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Select the NULL brush.
+ previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
+ return previous_brush_ != NULL;
+ }
+
+ // Set the opacity.
+ if (0 == SetBkMode(hdc_, OPAQUE)) {
+ NOTREACHED();
+ return false;
+ }
+
+ // Create and select the brush.
+ previous_brush_ = SelectObject(CreateSolidBrush(color));
+ return previous_brush_ != NULL;
+}
+
+bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width,
+ float stroke_miter, DWORD pen_style) {
+ DCHECK(previous_pen_ == NULL);
+ // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
+ // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
+ // instead.
+
+ // No pen case
+ if (!use_pen) {
+ previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
+ return previous_pen_ != NULL;
+ }
+
+ // Use the stock pen if the stroke width is 0.
+ if (stroke_width == 0) {
+ // Create a pen with the right color.
+ previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
+ return previous_pen_ != NULL;
+ }
+
+ // Load a custom pen.
+ LOGBRUSH brush;
+ brush.lbStyle = BS_SOLID;
+ brush.lbColor = color;
+ brush.lbHatch = 0;
+ HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
+ DCHECK(pen != NULL);
+ previous_pen_ = SelectObject(pen);
+ if (previous_pen_ == NULL)
+ return false;
+
+ if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
+ NOTREACHED();
+ return false;
+ }
+ return true;
+}
+
+void VectorDevice::Cleanup() {
+ if (previous_brush_) {
+ HGDIOBJ result = SelectObject(previous_brush_);
+ previous_brush_ = NULL;
+ if (result) {
+ BOOL res = DeleteObject(result);
+ DCHECK(res != 0);
+ }
+ }
+ if (previous_pen_) {
+ HGDIOBJ result = SelectObject(previous_pen_);
+ previous_pen_ = NULL;
+ if (result) {
+ BOOL res = DeleteObject(result);
+ DCHECK(res != 0);
+ }
+ }
+ // Remove any loaded path from the context.
+ AbortPath(hdc_);
+}
+
+HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) {
+ HGDIOBJ result = ::SelectObject(hdc_, object);
+ DCHECK(result != HGDI_ERROR);
+ if (result == HGDI_ERROR)
+ return NULL;
+ return result;
+}
+
+bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) {
+ // Make sure that for transparent color, no brush is used.
+ if (paint.getAlpha() == 0) {
+ // Test if it ever happen.
+ NOTREACHED();
+ use_brush = false;
+ }
+
+ return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
+}
+
+bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) {
+ // Make sure that for transparent color, no pen is used.
+ if (paint.getAlpha() == 0) {
+ // Test if it ever happen.
+ NOTREACHED();
+ use_pen = false;
+ }
+
+ DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
+ switch (paint.getStrokeJoin()) {
+ case SkPaint::kMiter_Join:
+ // Connects path segments with a sharp join.
+ pen_style |= PS_JOIN_MITER;
+ break;
+ case SkPaint::kRound_Join:
+ // Connects path segments with a round join.
+ pen_style |= PS_JOIN_ROUND;
+ break;
+ case SkPaint::kBevel_Join:
+ // Connects path segments with a flat bevel join.
+ pen_style |= PS_JOIN_BEVEL;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+ switch (paint.getStrokeCap()) {
+ case SkPaint::kButt_Cap:
+ // Begin/end contours with no extension.
+ pen_style |= PS_ENDCAP_FLAT;
+ break;
+ case SkPaint::kRound_Cap:
+ // Begin/end contours with a semi-circle extension.
+ pen_style |= PS_ENDCAP_ROUND;
+ break;
+ case SkPaint::kSquare_Cap:
+ // Begin/end contours with a half square extension.
+ pen_style |= PS_ENDCAP_SQUARE;
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return CreatePen(use_pen,
+ SkColorToCOLORREF(paint.getColor()),
+ SkScalarRound(paint.getStrokeWidth()),
+ paint.getStrokeMiter(),
+ pen_style);
+}
+
+void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint) {
+ uint8 alpha = paint.getAlpha();
+ if (alpha == 0)
+ return;
+
+ bool is_translucent;
+ if (alpha != 255) {
+ // ApplyPaint expect an opaque color.
+ SkPaint tmp_paint(paint);
+ tmp_paint.setAlpha(255);
+ if (!ApplyPaint(tmp_paint))
+ return;
+ is_translucent = true;
+ } else {
+ if (!ApplyPaint(paint))
+ return;
+ is_translucent = false;
+ }
+ int src_size_x = bitmap.width();
+ int src_size_y = bitmap.height();
+ if (!src_size_x || !src_size_y)
+ return;
+
+ // Create a BMP v4 header that we can serialize.
+ BITMAPV4HEADER bitmap_header;
+ gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header);
+ HDC dc = getBitmapDC();
+ SkAutoLockPixels lock(bitmap);
+ DCHECK_EQ(bitmap.getConfig(), SkBitmap::kARGB_8888_Config);
+ const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
+ if (pixels == NULL) {
+ NOTREACHED();
+ return;
+ }
+
+ if (!is_translucent) {
+ int row_length = bitmap.rowBytesAsPixels();
+ // There is no quick way to determine if an image is opaque.
+ for (int y2 = 0; y2 < src_size_y; ++y2) {
+ for (int x2 = 0; x2 < src_size_x; ++x2) {
+ if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
+ is_translucent = true;
+ y2 = src_size_y;
+ break;
+ }
+ }
+ }
+ }
+
+ BITMAPINFOHEADER hdr;
+ gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr);
+ if (is_translucent) {
+ // The image must be loaded as a bitmap inside a device context.
+ ScopedHDC bitmap_dc(::CreateCompatibleDC(dc));
+ void* bits = NULL;
+ ScopedBitmap hbitmap(::CreateDIBSection(
+ bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
+ DIB_RGB_COLORS, &bits, NULL, 0));
+ memcpy(bits, pixels, bitmap.getSize());
+ DCHECK(hbitmap);
+ HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
+ DeleteObject(old_bitmap);
+
+ // After some analysis of IE7's behavior, this is the thing to do. I was
+ // sure IE7 was doing so kind of bitmasking due to the way translucent image
+ // where renderered but after some windbg tracing, it is being done by the
+ // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
+ // for bitmasked images. The trick seems to switch the stretching mode in
+ // what the driver expects.
+ DWORD previous_mode = GetStretchBltMode(dc);
+ BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
+ DCHECK(result);
+ // Note that this function expect premultiplied colors (!)
+ BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
+ result = AlphaBlend(dc,
+ x, y, // Destination origin.
+ src_size_x, src_size_y, // Destination size.
+ bitmap_dc,
+ 0, 0, // Source origin.
+ src_size_x, src_size_y, // Source size.
+ blend_function);
+ DCHECK(result);
+ result = SetStretchBltMode(dc, previous_mode);
+ DCHECK(result);
+ } else {
+ BOOL result = StretchDIBits(dc,
+ x, y, // Destination origin.
+ src_size_x, src_size_y,
+ 0, 0, // Source origin.
+ src_size_x, src_size_y, // Source size.
+ pixels,
+ reinterpret_cast<const BITMAPINFO*>(&hdr),
+ DIB_RGB_COLORS,
+ SRCCOPY);
+ DCHECK(result);
+ }
+ Cleanup();
+}
+
+} // namespace gfx
diff --git a/base/gfx/vector_device.h b/base/gfx/vector_device.h
new file mode 100644
index 0000000..002ce17
--- /dev/null
+++ b/base/gfx/vector_device.h
@@ -0,0 +1,146 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_GFX_VECTOR_DEVICE_H__
+#define BASE_GFX_VECTOR_DEVICE_H__
+
+#include "base/basictypes.h"
+#include "base/gfx/platform_device.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
+
+namespace gfx {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. This specific device is not not backed by a surface
+// and is thus unreadable. This is because the backend is completely vectorial.
+// This device is a simple wrapper over a Windows device context (HDC) handle.
+class VectorDevice : public PlatformDevice {
+ public:
+ // Factory function. The DC is kept as the output context.
+ static VectorDevice* create(HDC dc, int width, int height);
+
+ VectorDevice(HDC dc, const SkBitmap& bitmap);
+ virtual ~VectorDevice();
+
+ virtual HDC getBitmapDC() {
+ return hdc_;
+ }
+
+ virtual void drawPaint(const SkDraw& draw, const SkPaint& paint);
+ virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
+ const SkPoint[], const SkPaint& paint);
+ virtual void drawRect(const SkDraw& draw, const SkRect& r,
+ const SkPaint& paint);
+ virtual void drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint);
+ virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint& paint);
+ virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint);
+ virtual void drawText(const SkDraw& draw, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint);
+ virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint);
+ virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint);
+ virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode, int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint);
+ virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y,
+ const SkPaint&);
+
+
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region);
+ virtual void setDeviceOffset(int x, int y);
+ virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect);
+ virtual bool IsVectorial() { return true; }
+
+ void LoadClipRegion();
+
+ private:
+ // Applies the SkPaint's painting properties in the current GDI context, if
+ // possible. If GDI can't support all paint's properties, returns false. It
+ // doesn't execute the "commands" in SkPaint.
+ bool ApplyPaint(const SkPaint& paint);
+
+ // Selects a new object in the device context. It can be a pen, a brush, a
+ // clipping region, a bitmap or a font. Returns the old selected object.
+ HGDIOBJ SelectObject(HGDIOBJ object);
+
+ // Creates a brush according to SkPaint's properties.
+ bool CreateBrush(bool use_brush, const SkPaint& paint);
+
+ // Creates a pen according to SkPaint's properties.
+ bool CreatePen(bool use_pen, const SkPaint& paint);
+
+ // Restores back the previous objects (pen, brush, etc) after a paint command.
+ void Cleanup();
+
+ // Creates a brush according to SkPaint's properties.
+ bool CreateBrush(bool use_brush, COLORREF color);
+
+ // Creates a pen according to SkPaint's properties.
+ bool CreatePen(bool use_pen, COLORREF color, int stroke_width,
+ float stroke_miter, DWORD pen_style);
+
+ // Draws a bitmap in the the device, using the currently loaded matrix.
+ void InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint);
+
+ // The Windows Device Context handle. It is the backend used with GDI drawing.
+ // This backend is write-only and vectorial.
+ HDC hdc_;
+
+ // Translation assigned to the DC: we need to keep track of this separately
+ // so it can be updated even if the DC isn't created yet.
+ SkMatrix transform_;
+
+ // The current clipping
+ SkRegion clip_region_;
+
+ // Previously selected brush before the current drawing.
+ HGDIOBJ previous_brush_;
+
+ // Previously selected pen before the current drawing.
+ HGDIOBJ previous_pen_;
+
+ int offset_x_;
+ int offset_y_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(VectorDevice);
+};
+
+} // namespace gfx
+
+#endif // BASE_GFX_VECTOR_DEVICE_H__
diff --git a/base/hash_tables.h b/base/hash_tables.h
new file mode 100644
index 0000000..13c201b
--- /dev/null
+++ b/base/hash_tables.h
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Deal with the differences between Microsoft and GNU implemenations
+// of hash_map
+//
+
+#ifndef BASE_HASH_TABLES_H__
+#define BASE_HASH_TABLES_H__
+
+#ifdef WIN32
+#include <hash_map>
+#include <hash_set>
+namespace base {
+ using stdext::hash_map;
+ using stdext::hash_set;
+}
+#else
+#include <ext/hash_map>
+#include <ext/hash_set>
+namespace base {
+ using __gnu_cxx::hash_map;
+ using __gnu_cxx::hash_set;
+}
+
+#endif
+
+#endif // BASE_HASH_TABLES_H__
diff --git a/base/histogram.cc b/base/histogram.cc
new file mode 100644
index 0000000..fc72f8f
--- /dev/null
+++ b/base/histogram.cc
@@ -0,0 +1,668 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+// See header file for details and examples.
+
+#include "base/histogram.h"
+
+#include <math.h>
+#include <string>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+
+typedef Histogram::Count Count;
+
+Histogram::Histogram(const wchar_t* name, Sample minimum,
+ Sample maximum, size_t bucket_count)
+ : StatsRate(name),
+ histogram_name_(WideToASCII(name)),
+ declared_min_(minimum),
+ declared_max_(maximum),
+ bucket_count_(bucket_count),
+ flags_(0),
+ ranges_(bucket_count + 1, 0),
+ sample_(),
+ registered_(false) {
+ Initialize();
+}
+
+Histogram::Histogram(const wchar_t* name, TimeDelta minimum,
+ TimeDelta maximum, size_t bucket_count)
+ : StatsRate(name),
+ histogram_name_(WideToASCII(name)),
+ declared_min_(static_cast<int> (minimum.InMilliseconds())),
+ declared_max_(static_cast<int> (maximum.InMilliseconds())),
+ bucket_count_(bucket_count),
+ flags_(0),
+ ranges_(bucket_count + 1, 0),
+ sample_(),
+ registered_(false) {
+ Initialize();
+}
+
+Histogram::~Histogram() {
+ if (registered_)
+ StatisticsRecorder::UnRegister(*this);
+ // Just to make sure most derived class did this properly...
+ DCHECK(ValidateBucketRanges());
+}
+
+
+// Hooks to override stats counter methods. This ensures that we gather all
+// input the stats counter sees.
+void Histogram::Add(int value) {
+ if (!registered_)
+ registered_ = StatisticsRecorder::Register(*this);
+ if (value >= kSampleType_MAX)
+ value = kSampleType_MAX - 1;
+ StatsRate::Add(value);
+ if (value < 0)
+ value = 0;
+ size_t index = BucketIndex(value);
+ DCHECK(value >= ranges(index));
+ DCHECK(value < ranges(index + 1));
+ Accumulate(value, 1, index);
+}
+
+// The following methods provide a graphical histogram display.
+void Histogram::WriteHTMLGraph(std::string* output) const {
+ // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc.
+ output->append("<PRE>");
+ WriteAscii(true, "<br>", output);
+ output->append("</PRE>");
+}
+
+void Histogram::WriteAscii(bool graph_it, const std::string& newline,
+ std::string* output) const {
+ // Get local (stack) copies of all effectively volatile class data so that we
+ // are consistent across our output activities.
+ SampleSet snapshot;
+ SnapshotSample(&snapshot);
+ Count sample_count = snapshot.TotalCount();
+
+ WriteAsciiHeader(snapshot, sample_count, output);
+ output->append(newline);
+
+ // Prepare to normalize graphical rendering of bucket contents.
+ double max_size = 0;
+ if (graph_it)
+ max_size = GetPeakBucketSize(snapshot);
+
+ // Calculate space needed to print bucket range numbers. Leave room to print
+ // nearly the largest bucket range without sliding over the histogram.
+ size_t largest_non_empty_bucket = bucket_count_ - 1;
+ while (0 == sample_.counts(largest_non_empty_bucket)) {
+ if (0 == largest_non_empty_bucket)
+ break; // All buckets are empty.
+ largest_non_empty_bucket--;
+ }
+
+ // Calculate largest print width needed for any of our bucket range displays.
+ size_t print_width = 1;
+ for (size_t i = 0; i < bucket_count_; ++i) {
+ if (snapshot.counts(i)) {
+ size_t width = GetAsciiBucketRange(i).size() + 1;
+ if (width > print_width)
+ print_width = width;
+ }
+ }
+
+ int64 remaining = sample_count;
+ int64 past = 0;
+ // Output the actual histogram graph.
+ for (size_t i = 0; i < bucket_count_; i++) {
+ Count current = snapshot.counts(i);
+ if (!current && !PrintEmptyBucket(i))
+ continue;
+ remaining -= current;
+ StringAppendF(output, "%#*s ", print_width, GetAsciiBucketRange(i).c_str());
+ if (0 == current && i < bucket_count_ - 1 && 0 == snapshot.counts(i + 1)) {
+ while (i < bucket_count_ - 1 && 0 == snapshot.counts(i + 1))
+ i++;
+ output->append("... ");
+ output->append(newline);
+ continue; // No reason to plot emptiness.
+ }
+ double current_size = GetBucketSize(current, i);
+ if (graph_it)
+ WriteAsciiBucketGraph(current_size, max_size, output);
+ WriteAsciiBucketContext(past, current, remaining, i, output);
+ output->append(newline);
+ past += current;
+ }
+ DCHECK(past == sample_count);
+}
+
+bool Histogram::ValidateBucketRanges() const {
+ // Standard assertions that all bucket ranges should satisfy.
+ DCHECK(ranges_.size() == bucket_count_ + 1);
+ DCHECK(0 == ranges_[0]);
+ DCHECK(declared_min() == ranges_[1]);
+ DCHECK(declared_max() == ranges_[bucket_count_ - 1]);
+ DCHECK(kSampleType_MAX == ranges_[bucket_count_]);
+ return true;
+}
+
+void Histogram::Initialize() {
+ sample_.Resize(*this);
+ if (declared_min_ <= 0)
+ declared_min_ = 1;
+ if (declared_max_ >= kSampleType_MAX)
+ declared_max_ = kSampleType_MAX - 1;
+ DCHECK(declared_min_ > 0); // We provide underflow bucket.
+ DCHECK(declared_min_ < declared_max_);
+ DCHECK(1 < bucket_count_);
+ size_t maximal_bucket_count = declared_max_ - declared_min_ + 2;
+ DCHECK(bucket_count_ <= maximal_bucket_count);
+ DCHECK(0 == ranges_[0]);
+ ranges_[bucket_count_] = kSampleType_MAX;
+ InitializeBucketRange();
+ DCHECK(ValidateBucketRanges());
+ registered_ = StatisticsRecorder::Register(*this);
+}
+
+// Calculate what range of values are held in each bucket.
+// We have to be careful that we don't pick a ratio between starting points in
+// consecutive buckets that is sooo small, that the integer bounds are the same
+// (effectively making one bucket get no values). We need to avoid:
+// (ranges_[i] == ranges_[i + 1]
+// To avoid that, we just do a fine-grained bucket width as far as we need to
+// until we get a ratio that moves us along at least 2 units at a time. From
+// that bucket onward we do use the exponential growth of buckets.
+void Histogram::InitializeBucketRange() {
+ double log_max = log(static_cast<double>(declared_max()));
+ double log_ratio;
+ double log_next;
+ size_t bucket_index = 1;
+ Sample current = declared_min();
+ SetBucketRange(bucket_index, current);
+ while (bucket_count() > ++bucket_index) {
+ double log_current;
+ log_current = log(static_cast<double>(current));
+ // Calculate the count'th root of the range.
+ log_ratio = (log_max - log_current) / (bucket_count() - bucket_index);
+ // See where the next bucket would start.
+ log_next = log_current + log_ratio;
+ int next;
+ next = static_cast<int>(floor(exp(log_next) + 0.5));
+ if (next > current)
+ current = next;
+ else
+ current++; // Just do a narrow bucket, and keep trying.
+ SetBucketRange(bucket_index, current);
+ }
+
+ DCHECK(bucket_count() == bucket_index);
+}
+
+size_t Histogram::BucketIndex(Sample value) const {
+ // Use simple binary search. This is very general, but there are better
+ // approaches if we knew that the buckets were linearly distributed.
+ DCHECK(ranges(0) <= value);
+ DCHECK(ranges(bucket_count()) > value);
+ size_t under = 0;
+ size_t over = bucket_count();
+ size_t mid;
+
+ do {
+ DCHECK(over >= under);
+ mid = (over + under)/2;
+ if (mid == under)
+ break;
+ if (ranges(mid) <= value)
+ under = mid;
+ else
+ over = mid;
+ } while (true);
+
+ DCHECK(ranges(mid) <= value && ranges(mid+1) > value);
+ return mid;
+}
+
+// Use the actual bucket widths (like a linear histogram) until the widths get
+// over some transition value, and then use that transition width. Exponentials
+// get so big so fast (and we don't expect to see a lot of entries in the large
+// buckets), so we need this to make it possible to see what is going on and
+// not have 0-graphical-height buckets.
+double Histogram::GetBucketSize(Count current, size_t i) const {
+ DCHECK(ranges(i + 1) > ranges(i));
+ static const double kTransitionWidth = 5;
+ double denominator = ranges(i + 1) - ranges(i);
+ if (denominator > kTransitionWidth)
+ denominator = kTransitionWidth; // Stop trying to normalize.
+ return current/denominator;
+}
+
+//------------------------------------------------------------------------------
+// The following two methods can be overridden to provide a thread safe
+// version of this class. The cost of locking is low... but an error in each
+// of these methods has minimal impact. For now, I'll leave this unlocked,
+// and I don't believe I can loose more than a count or two.
+// The vectors are NOT reallocated, so there is no risk of them moving around.
+
+// Update histogram data with new sample.
+void Histogram::Accumulate(Sample value, Count count, size_t index) {
+ // Note locking not done in this version!!!
+ sample_.Accumulate(value, count, index);
+}
+
+// Do a safe atomic snapshot of sample data.
+// This implementation assumes we are on a safe single thread.
+void Histogram::SnapshotSample(SampleSet* sample) const {
+ // Note locking not done in this version!!!
+ *sample = sample_;
+}
+
+//------------------------------------------------------------------------------
+// Accessor methods
+
+void Histogram::SetBucketRange(size_t i, Sample value) {
+ DCHECK(bucket_count_ > i);
+ ranges_[i] = value;
+}
+
+//------------------------------------------------------------------------------
+// Private methods
+
+double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const {
+ double max = 0;
+ for (size_t i = 0; i < bucket_count_ ; i++) {
+ double current_size = GetBucketSize(snapshot.counts(i), i);
+ if (current_size > max)
+ max = current_size;
+ }
+ return max;
+}
+
+void Histogram::WriteAsciiHeader(const SampleSet& snapshot,
+ Count sample_count,
+ std::string* output) const {
+ StringAppendF(output,
+ "Histogram: %s recorded %ld samples",
+ histogram_name().c_str(),
+ sample_count);
+ if (0 == sample_count) {
+ DCHECK(0 == snapshot.sum());
+ } else {
+ double average = static_cast<float>(snapshot.sum()) / sample_count;
+ double variance = static_cast<float>(snapshot.square_sum())/sample_count
+ - average * average;
+ double standard_deviation = sqrt(variance);
+
+ StringAppendF(output,
+ ", average = %.1f, standard deviation = %.1f",
+ average, standard_deviation);
+ }
+ if (flags_ & ~kHexRangePrintingFlag )
+ StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag);
+}
+
+void Histogram::WriteAsciiBucketContext(const int64 past,
+ const Count current,
+ const int64 remaining,
+ const size_t i,
+ std::string* output) const {
+ double scaled_sum = (past + current + remaining) / 100.0;
+ WriteAsciiBucketValue(current, scaled_sum, output);
+ if (0 < i) {
+ double percentage = past / scaled_sum;
+ StringAppendF(output, " {%3.1f%%}", percentage);
+ }
+}
+
+const std::string Histogram::GetAsciiBucketRange(size_t i) const {
+ std::string result;
+ if (kHexRangePrintingFlag & flags_)
+ StringAppendF(&result, "%#x", ranges_[i]);
+ else
+ StringAppendF(&result, "%d", ranges_[i]);
+ return result;
+}
+
+void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum,
+ std::string* output) const {
+ StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum);
+}
+
+void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
+ std::string* output) const {
+ const int k_line_length = 72; // Maximal horizontal width of graph.
+ int x_count = static_cast<int>(k_line_length * (current_size / max_size)
+ + 0.5);
+ int x_remainder = k_line_length - x_count;
+
+ while (0 < x_count--)
+ output->append("-");
+ output->append("O");
+ while (0 < x_remainder--)
+ output->append(" ");
+}
+
+//------------------------------------------------------------------------------
+// Methods for the Histogram::SampleSet class
+//------------------------------------------------------------------------------
+
+Histogram::SampleSet::SampleSet()
+ : counts_(),
+ sum_(0),
+ square_sum_(0) {
+}
+
+void Histogram::SampleSet::Resize(const Histogram& histogram) {
+ counts_.resize(histogram.bucket_count(), 0);
+}
+
+void Histogram::SampleSet::CheckSize(const Histogram& histogram) const {
+ DCHECK(counts_.size() == histogram.bucket_count());
+}
+
+
+void Histogram::SampleSet::Accumulate(Sample value, Count count,
+ size_t index) {
+ DCHECK(count == 1 || count == -1);
+ counts_[index] += count;
+ sum_ += count * value;
+ square_sum_ += (count * value) * static_cast<int64>(value);
+ DCHECK(counts_[index] >= 0);
+ DCHECK(sum_ >= 0);
+ DCHECK(square_sum_ >= 0);
+}
+
+Count Histogram::SampleSet::TotalCount() const {
+ Count total = 0;
+ for (Counts::const_iterator it = counts_.begin();
+ it != counts_.end();
+ it++) {
+ total += *it;
+ }
+ return total;
+}
+
+void Histogram::SampleSet::Add(const SampleSet& other) {
+ DCHECK(counts_.size() == other.counts_.size());
+ sum_ += other.sum_;
+ square_sum_ += other.square_sum_;
+ for (size_t index = 0; index < counts_.size(); index++)
+ counts_[index] += other.counts_[index];
+}
+
+void Histogram::SampleSet::Subtract(const SampleSet& other) {
+ DCHECK(counts_.size() == other.counts_.size());
+ // Note: Race conditions in snapshotting a sum or square_sum may lead to
+ // (temporary) negative values when snapshots are later combined (and deltas
+ // calculated). As a result, we don't currently CHCEK() for positive values.
+ sum_ -= other.sum_;
+ square_sum_ -= other.square_sum_;
+ for (size_t index = 0; index < counts_.size(); index++) {
+ counts_[index] -= other.counts_[index];
+ DCHECK(counts_[index] >= 0);
+ }
+}
+
+//------------------------------------------------------------------------------
+// LinearHistogram: This histogram uses a traditional set of evenly spaced
+// buckets.
+//------------------------------------------------------------------------------
+
+LinearHistogram::LinearHistogram(const wchar_t* name,
+ Sample minimum, Sample maximum, size_t bucket_count)
+ : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) {
+ InitializeBucketRange();
+ DCHECK(ValidateBucketRanges());
+}
+
+LinearHistogram::LinearHistogram(const wchar_t* name,
+ TimeDelta minimum, TimeDelta maximum, size_t bucket_count)
+ : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ?
+ minimum : TimeDelta::FromMilliseconds(1),
+ maximum, bucket_count) {
+ // Do a "better" (different) job at init than a base classes did...
+ InitializeBucketRange();
+ DCHECK(ValidateBucketRanges());
+}
+
+void LinearHistogram::SetRangeDescriptions(const DescriptionPair descriptions[]) {
+ for (int i =0; descriptions[i].description; ++i) {
+ bucket_description_[descriptions[i].sample] = descriptions[i].description;
+ }
+}
+
+const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const {
+ int range = ranges(i);
+ BucketDescriptionMap::const_iterator it = bucket_description_.find(range);
+ if (it == bucket_description_.end())
+ return Histogram::GetAsciiBucketRange(i);
+ return it->second;
+}
+
+bool LinearHistogram::PrintEmptyBucket(size_t index) const {
+ return bucket_description_.find(ranges(index)) == bucket_description_.end();
+}
+
+
+void LinearHistogram::InitializeBucketRange() {
+ DCHECK(0 < declared_min()); // 0 is the underflow bucket here.
+ double min = declared_min();
+ double max = declared_max();
+ size_t i;
+ for (i = 1; i < bucket_count(); i++) {
+ double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) /
+ (bucket_count() - 2);
+ SetBucketRange(i, static_cast<int> (linear_range + 0.5));
+ }
+}
+
+// Find bucket to increment for sample value.
+size_t LinearHistogram::BucketIndex(Sample value) const {
+ if (value < declared_min()) return 0;
+ if (value >= declared_max()) return bucket_count() - 1;
+ size_t index;
+ index = static_cast<size_t>(((value - declared_min()) * (bucket_count() - 2))
+ / (declared_max() - declared_min()) + 1);
+ DCHECK(1 <= index && bucket_count() > index);
+ return index;
+}
+
+double LinearHistogram::GetBucketSize(Count current, size_t i) const {
+ DCHECK(ranges(i + 1) > ranges(i));
+ // Adjacent buckets with different widths would have "surprisingly" many (few)
+ // samples in a histogram if we didn't normalize this way.
+ double denominator = ranges(i + 1) - ranges(i);
+ return current/denominator;
+}
+
+//------------------------------------------------------------------------------
+// This section provides implementation for ThreadSafeHistogram.
+//------------------------------------------------------------------------------
+
+ThreadSafeHistogram::ThreadSafeHistogram(const wchar_t* name, Sample minimum,
+ Sample maximum, size_t bucket_count)
+ : Histogram(name, minimum, maximum, bucket_count),
+ lock_() {
+ }
+
+void ThreadSafeHistogram::Remove(int value) {
+ if (value >= kSampleType_MAX)
+ value = kSampleType_MAX - 1;
+ StatsRate::Add(-value);
+ size_t index = BucketIndex(value);
+ Accumulate(value, -1, index);
+}
+
+void ThreadSafeHistogram::Accumulate(Sample value, Count count, size_t index) {
+ AutoLock lock(lock_);
+ Histogram::Accumulate(value, count, index);
+}
+
+void ThreadSafeHistogram::SnapshotSample(SampleSet* sample) {
+ AutoLock lock(lock_);
+ Histogram::SnapshotSample(sample);
+};
+
+
+//------------------------------------------------------------------------------
+// The next section handles global (central) support for all histograms, as well
+// as startup/teardown of this service.
+//------------------------------------------------------------------------------
+
+// This singleton instance should be started during the single threaded portion
+// of main(), and hence it is not thread safe. It initializes globals to
+// provide support for all future calls.
+StatisticsRecorder::StatisticsRecorder() {
+ DCHECK(!histograms_);
+ lock_ = new Lock;
+ histograms_ = new HistogramMap;
+}
+
+StatisticsRecorder::~StatisticsRecorder() {
+ DCHECK(histograms_);
+
+ if (dump_on_exit_) {
+ std::string output;
+ WriteGraph("", &output);
+ LOG(INFO) << output;
+ }
+
+ // Clean up.
+ delete histograms_;
+ histograms_ = NULL;
+ delete lock_;
+ lock_ = NULL;
+}
+
+// static
+bool StatisticsRecorder::WasStarted() {
+ return NULL != histograms_;
+}
+
+// static
+bool StatisticsRecorder::Register(const Histogram& histogram) {
+ if (!histograms_)
+ return false;
+ const std::string name = histogram.histogram_name();
+ AutoLock auto_lock(*lock_);
+ DCHECK(histograms_->end() == histograms_->find(name)); // Only register once.
+ (*histograms_)[name] = &histogram;
+ return true;
+}
+
+// static
+void StatisticsRecorder::UnRegister(const Histogram& histogram) {
+ if (!histograms_)
+ return;
+ const std::string name = histogram.histogram_name();
+ AutoLock auto_lock(*lock_);
+ DCHECK(histograms_->end() != histograms_->find(name));
+ histograms_->erase(name);
+ if (dump_on_exit_) {
+ std::string output;
+ histogram.WriteAscii(true, "\n", &output);
+ LOG(INFO) << output;
+ }
+}
+
+// static
+void StatisticsRecorder::WriteHTMLGraph(const std::string& query,
+ std::string* output) {
+ if (!histograms_)
+ return;
+ output->append("<html><head><title>About Histograms");
+ if (!query.empty())
+ output->append(" - " + query);
+ output->append("</title>"
+ // We'd like the following no-cache... but it doesn't work.
+ // "<META HTTP-EQUIV=\"Pragma\" CONTENT=\"no-cache\">"
+ "</head><body>");
+
+ Histograms snapshot;
+ GetSnapshot(query, &snapshot);
+ for (Histograms::iterator it = snapshot.begin();
+ it != snapshot.end();
+ it++) {
+ (*it)->WriteHTMLGraph(output);
+ output->append("<br><hr><br>");
+ }
+ output->append("</body></html>");
+}
+
+// static
+void StatisticsRecorder::WriteGraph(const std::string& query,
+ std::string* output) {
+ if (!histograms_)
+ return;
+ if (query.length())
+ StringAppendF(output, "Collections of histograms for %s\n", query.c_str());
+ else
+ output->append("Collections of all histograms\n");
+
+ Histograms snapshot;
+ GetSnapshot(query, &snapshot);
+ for (Histograms::iterator it = snapshot.begin();
+ it != snapshot.end();
+ it++) {
+ (*it)->WriteAscii(true, "\n", output);
+ output->append("\n");
+ }
+}
+
+// static
+void StatisticsRecorder::GetHistograms(Histograms* output) {
+ if (!histograms_)
+ return;
+ AutoLock auto_lock(*lock_);
+ for (HistogramMap::iterator it = histograms_->begin();
+ histograms_->end() != it;
+ it++) {
+ output->push_back(it->second);
+ }
+}
+
+// private static
+void StatisticsRecorder::GetSnapshot(const std::string& query,
+ Histograms* snapshot) {
+ AutoLock auto_lock(*lock_);
+ for (HistogramMap::iterator it = histograms_->begin();
+ histograms_->end() != it;
+ it++) {
+ if (it->first.find(query) != std::string::npos)
+ snapshot->push_back(it->second);
+ }
+}
+
+// static
+StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL;
+// static
+Lock* StatisticsRecorder::lock_ = NULL;
+// static
+bool StatisticsRecorder::dump_on_exit_ = false;
diff --git a/base/histogram.h b/base/histogram.h
new file mode 100644
index 0000000..295f610
--- /dev/null
+++ b/base/histogram.h
@@ -0,0 +1,469 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Histogram is an object that aggregates statistics, and can summarize them in
+// various forms, including ASCII graphical, HTML, and numerically (as a
+// vector of numbers corresponding to each of the aggregating buckets).
+
+// It supports calls to accumulate either time intervals (which are processed
+// as integral number of milliseconds), or arbitrary integral units.
+
+// The default layout of buckets is exponential. For example, buckets might
+// contain (sequentially) the count of values in the following intervals:
+// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity)
+// That bucket allocation would actually result from construction of a histogram
+// for values between 1 and 64, with 8 buckets, such as:
+// Histogram count(L"some name", 1, 64, 8);
+// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity)
+// are not counted by the constructor in the user supplied "bucket_count"
+// argument.
+// The above example has an exponential ratio of 2 (doubling the bucket width
+// in each consecutive bucket. The Histogram class automatically calculates
+// the smallest ratio that it can use to construct the number of buckets
+// selected in the constructor. An another example, if you had 50 buckets,
+// and millisecond time values from 1 to 10000, then the ratio between
+// consecutive bucket widths will be approximately somewhere around the 50th
+// root of 10000. This approach provides very fine grain (narrow) buckets
+// at the low end of the histogram scale, but allows the histogram to cover a
+// gigantic range with the addition of very few buckets.
+
+#ifndef BASE_HISTOGRAM_H__
+#define BASE_HISTOGRAM_H__
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/stats_counters.h"
+
+//------------------------------------------------------------------------------
+// Provide easy general purpose histogram in a macro, just like stats counters.
+// These macros all use 50 buckets.
+
+#define HISTOGRAM_TIMES(name, sample) do { \
+ static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+ TimeDelta::FromSeconds(10), 50); \
+ counter.AddTime(sample); \
+ } while (0)
+
+#define HISTOGRAM_COUNTS(name, sample) do { \
+ static Histogram counter((name), 1, 1000000, 50); \
+ counter.Add(sample); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// This macro set is for a histogram that can support both addition and removal
+// of samples. It should be used to render the accumulated asset allocation
+// of some samples. For example, it can sample memory allocation sizes, and
+// memory releases (as negative samples).
+// To simplify the interface, only non-zero values can be sampled, with positive
+// numbers indicating addition, and negative numbers implying dimunition
+// (removal).
+// Note that the underlying ThreadSafeHistogram() uses locking to ensure that
+// counts are precise (no chance of losing an addition or removal event, due to
+// multithread racing). This precision is required to prevent missed-counts from
+// resulting in drift, as the calls to Remove() for a given value should always
+// be equal in number or fewer than the corresponding calls to Add().
+
+#define ASSET_HISTOGRAM_COUNTS(name, sample) do { \
+ static ThreadSafeHistogram counter((name), 1, 1000000, 50); \
+ if (0 == sample) break; \
+ if (sample >= 0) \
+ counter.Add(sample); \
+ else\
+ counter.Remove(-sample); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample)
+#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample)
+#define DASSET_HISTOGRAM_COUNTS(name, sample) ASSET_HISTOGRAM_COUNTS(name, \
+ sample)
+
+#else // NDEBUG
+
+#define DHISTOGRAM_TIMES(name, sample) do {} while (0)
+#define DHISTOGRAM_COUNTS(name, sample) do {} while (0)
+#define DASSET_HISTOGRAM_COUNTS(name, sample) do {} while (0)
+
+#endif // NDEBUG
+
+//------------------------------------------------------------------------------
+// The following macros provide typical usage scenarios for callers that wish
+// to record histogram data, and have the data submitted/uploaded via UMA.
+// Not all systems support such UMA, but if they do, the following macros
+// should work with the service.
+
+static const int kUmaTargetedHistogramFlag = 0x1;
+
+#define UMA_HISTOGRAM_TIMES(name, sample) do { \
+ static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+ TimeDelta::FromSeconds(10), 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.AddTime(sample); \
+ } while (0)
+
+// Use this macro when times can routinely be much longer than 10 seconds.
+#define UMA_HISTOGRAM_LONG_TIMES(name, sample) do { \
+ static Histogram counter((name), TimeDelta::FromMilliseconds(1), \
+ TimeDelta::FromHours(1), 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.AddTime(sample); \
+ } while (0)
+
+#define UMA_HISTOGRAM_COUNTS(name, sample) do { \
+ static Histogram counter((name), 1, 1000000, 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.Add(sample); \
+ } while (0)
+
+#define UMA_HISTOGRAM_COUNTS_100(name, sample) do { \
+ static Histogram counter((name), 1, 100, 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.Add(sample); \
+ } while (0)
+
+#define UMA_HISTOGRAM_MEMORY_KB(name, sample) do { \
+ static Histogram counter((name), 1000, 500000, 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.Add(sample); \
+ } while (0)
+
+#define UMA_HISTOGRAM_MEMORY_MB(name, sample) do { \
+ static Histogram counter((name), 1, 1000, 50); \
+ counter.SetFlags(kUmaTargetedHistogramFlag); \
+ counter.Add(sample); \
+ } while (0)
+
+//------------------------------------------------------------------------------
+
+class Histogram : public StatsRate {
+ public:
+ typedef int Sample; // Used for samples (and ranges of samples).
+ typedef int Count; // Used to count samples in a bucket.
+ static const Sample kSampleType_MAX = INT_MAX;
+
+ typedef std::vector<Count> Counts;
+ typedef std::vector<const Sample> Ranges;
+
+ static const int kHexRangePrintingFlag = 0x8000;
+ //----------------------------------------------------------------------------
+ // Statistic values, developed over the life of the histogram.
+
+ class SampleSet {
+ public:
+ explicit SampleSet();
+ // Adjust size of counts_ for use with given histogram.
+ void Resize(const Histogram& histogram);
+ void CheckSize(const Histogram& histogram) const;
+
+ // Accessor for histogram to make routine additions.
+ void Accumulate(Sample value, Count count, size_t index);
+
+ // Accessor methods.
+ Count counts(size_t i) const { return counts_[i]; }
+ Count TotalCount() const ;
+ int64 sum() const { return sum_; }
+ int64 square_sum() const { return square_sum_; }
+
+ // Arithmetic manipulation of corresponding elements of the set.
+ void Add(const SampleSet& other);
+ void Subtract(const SampleSet& other);
+
+ private:
+ // Actual histogram data is stored in buckets, showing the count of values
+ // that fit into each bucket.
+ Counts counts_;
+
+ // Save simple stats locally. Note that this MIGHT get done in base class
+ // without shared memory at some point.
+ int64 sum_; // sum of samples.
+ int64 square_sum_; // sum of squares of samples.
+ };
+ //----------------------------------------------------------------------------
+
+ Histogram(const wchar_t* name, Sample minimum,
+ Sample maximum, size_t bucket_count);
+ Histogram(const wchar_t* name, TimeDelta minimum,
+ TimeDelta maximum, size_t bucket_count);
+ ~Histogram();
+
+ // Hooks to override stats counter methods. This ensures that we gather all
+ // input the stats counter sees.
+ virtual void Add(int value);
+
+ // The following methods provide a graphical histogram displays.
+ void WriteHTMLGraph(std::string* output) const;
+ void WriteAscii(bool graph_it, const std::string& newline,
+ std::string* output) const;
+
+ // Support generic flagging of Histograms.
+ // 0x1 Currently used to mark this histogram to be recorded by UMA..
+ // 0x8000 means print ranges in hex.
+ void SetFlags(int flags) { flags_ |= flags; }
+ int flags() const { return flags_; }
+
+ //----------------------------------------------------------------------------
+ // Accessors for serialization and testing.
+ //----------------------------------------------------------------------------
+ const std::string histogram_name() const { return histogram_name_; }
+ Sample declared_min() const { return declared_min_; }
+ Sample declared_max() const { return declared_max_; }
+ Sample ranges(size_t i) const { return ranges_[i];}
+ size_t bucket_count() const { return bucket_count_; }
+ // Snapshot the current complete set of sample data.
+ // Override with atomic/locked snapshot if needed.
+ virtual void SnapshotSample(SampleSet* sample) const;
+
+ protected:
+ // Method to override to skip the display of the i'th bucket if it's empty.
+ virtual bool PrintEmptyBucket(size_t index) const { return true; }
+
+ //----------------------------------------------------------------------------
+ // Methods to override to create histogram with different bucket widths.
+ //----------------------------------------------------------------------------
+ // Initialize ranges_ mapping.
+ virtual void InitializeBucketRange();
+ // Find bucket to increment for sample value.
+ virtual size_t BucketIndex(Sample value) const;
+ // Get normalized size, relative to the ranges_[i].
+ virtual double GetBucketSize(Count current, size_t i) const;
+
+ // Return a string description of what goes in a given bucket.
+ // Most commonly this is the numeric value, but in derived classes it may
+ // be a name (or string description) given to the bucket.
+ virtual const std::string GetAsciiBucketRange(size_t it) const;
+
+ //----------------------------------------------------------------------------
+ // Methods to override to create thread safe histogram.
+ //----------------------------------------------------------------------------
+ // Update all our internal data, including histogram
+ virtual void Accumulate(Sample value, Count count, size_t index);
+
+ //----------------------------------------------------------------------------
+ // Accessors for derived classes.
+ //----------------------------------------------------------------------------
+ void SetBucketRange(size_t i, Sample value);
+
+ // Validate that ranges_ was created sensibly (top and bottom range
+ // values relate properly to the declared_min_ and declared_max_)..
+ bool ValidateBucketRanges() const;
+
+ private:
+ // Post constructor initialization.
+ void Initialize();
+
+ //----------------------------------------------------------------------------
+ // Helpers for emitting Ascii graphic. Each method appends data to output.
+
+ // Find out how large the (graphically) the largest bucket will appear to be.
+ double GetPeakBucketSize(const SampleSet& snapshot) const;
+
+ // Write a common header message describing this histogram.
+ void WriteAsciiHeader(const SampleSet& snapshot,
+ Count sample_count, std::string* output) const ;
+
+ // Write information about previous, current, and next buckets.
+ // Information such as cumulative percentage, etc.
+ void WriteAsciiBucketContext(const int64 past, const Count current,
+ const int64 remaining, const size_t i,
+ std::string* output) const;
+
+ // Write textual description of the bucket contents (relative to histogram).
+ // Output is the count in the buckets, as well as the percentage.
+ void WriteAsciiBucketValue(Count current, double scaled_sum,
+ std::string* output) const;
+
+ // Produce actual graph (set of blank vs non blank char's) for a bucket.
+ void WriteAsciiBucketGraph(double current_size, double max_size,
+ std::string* output) const;
+
+ //----------------------------------------------------------------------------
+ // Invariant values set at/near construction time
+
+ // ASCII version of original name given to the constructor. All identically
+ // named instances will be coalesced cross-project TODO(jar).
+ // If a user needs one histogram name to be called by several places in a
+ // single process, a central function should be defined by teh user, which
+ // defins the single declared instance of the named histogram.
+ const std::string histogram_name_;
+ Sample declared_min_; // Less than this goes into counts_[0]
+ Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1].
+ size_t bucket_count_; // Dimension of counts_[].
+
+ // Flag the histogram for recording by UMA via metric_services.h.
+ int flags_;
+
+ // For each index, show the least value that can be stored in the
+ // corresponding bucket. We also append one extra element in this array,
+ // containing kSampleType_MAX, to make calculations easy.
+ // The dimension of ranges_ is bucket_count + 1.
+ Ranges ranges_;
+
+ // Finally, provide the state that changes with the addition of each new
+ // sample.
+ SampleSet sample_;
+
+ // Indicate if successfully registered.
+ bool registered_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Histogram);
+};
+
+//------------------------------------------------------------------------------
+
+// LinearHistogram is a more traditional histogram, with evenly spaced
+// buckets.
+class LinearHistogram : public Histogram {
+ public:
+ struct DescriptionPair {
+ Sample sample;
+ char* description; // Null means end of a list of pairs.
+ };
+ LinearHistogram(const wchar_t* name, Sample minimum,
+ Sample maximum, size_t bucket_count);
+ LinearHistogram(const wchar_t* name, TimeDelta minimum,
+ TimeDelta maximum, size_t bucket_count);
+ ~LinearHistogram() {}
+
+ // Store a list of number/text values for use in rendering the histogram.
+ // The last element in the array has a null in its "description" slot.
+ void SetRangeDescriptions(const DescriptionPair descriptions[]);
+
+ protected:
+ // Initialize ranges_ mapping.
+ virtual void InitializeBucketRange();
+ // Find bucket to increment for sample value.
+ virtual size_t BucketIndex(Sample value) const;
+ virtual double LinearHistogram::GetBucketSize(Count current,
+ size_t i) const;
+
+ // If we have a description for a bucket, then return that. Otherwise
+ // let parent class provide a (numeric) description.
+ virtual const std::string GetAsciiBucketRange(size_t i) const;
+
+ // Skip printing of name for numeric range if we have a name (and if this is
+ // an empty bucket).
+ virtual bool PrintEmptyBucket(size_t index) const;
+
+ private:
+ // For some ranges, we store a printable description of a bucket range.
+ // If there is no desciption, then GetAsciiBucketRange() uses parent class
+ // to provide a description.
+ typedef std::map<Sample, std::string> BucketDescriptionMap;
+ BucketDescriptionMap bucket_description_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(LinearHistogram);
+};
+
+
+//------------------------------------------------------------------------------
+// This section provides implementation for ThreadSafeHistogram.
+//------------------------------------------------------------------------------
+
+class ThreadSafeHistogram : public Histogram {
+ public:
+ ThreadSafeHistogram(const wchar_t* name, Sample minimum,
+ Sample maximum, size_t bucket_count);
+
+ // Provide the analog to Add()
+ void Remove(int value);
+
+ protected:
+ // Provide locked versions to get precise counts.
+ virtual void Accumulate(Sample value, Count count, size_t index);
+
+ virtual void SnapshotSample(SampleSet* sample);
+
+ private:
+ Lock lock_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadSafeHistogram);
+};
+
+//------------------------------------------------------------------------------
+// StatisticsRecorder handles all histograms in the system. It provides a
+// general place for histograms to register, and supports a global API for
+// accessing (i.e., dumping, or graphing) the data in all the histograms.
+
+class StatisticsRecorder {
+ public:
+ typedef std::vector<const Histogram*> Histograms;
+
+ StatisticsRecorder();
+
+ ~StatisticsRecorder();
+
+ // Find out if histograms can now be registered into our list.
+ static bool WasStarted();
+
+ // Register, or add a new histogram to the collection of statistics.
+ // Return true if registered.
+ static bool Register(const Histogram& histogram);
+ // Unregister, or remove, a histogram from the collection of statistics.
+ static void UnRegister(const Histogram& histogram);
+
+ // Methods for printing histograms. Only histograms which have query as
+ // a substring are written to output (an empty string will process all
+ // registered histograms).
+ static void WriteHTMLGraph(const std::string& query, std::string* output);
+ static void WriteGraph(const std::string& query, std::string* output);
+
+ // Method for extracting histograms which were marked for use by UMA.
+ static void GetHistograms(Histograms* output);
+
+ static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; }
+
+ private:
+ typedef std::map<std::string, const Histogram*> HistogramMap;
+ // We keep all registered histograms in a map, from name to histogram.
+
+ // GetSnapshot copies some of the pointers to registered histograms into the
+ // caller supplied vector (Histograms). Only histograms with names matching
+ // query are returned. The query must be a substring of histogram name for its
+ // pointer to be copied.
+ static void GetSnapshot(const std::string& query, Histograms* snapshot);
+
+ static HistogramMap* histograms_;
+ // lock protects access to the above map.
+ static Lock* lock_;
+
+ // Dump all known histograms to log.
+ static bool dump_on_exit_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StatisticsRecorder);
+};
+
+#endif // BASE_HISTOGRAM_H__
+
diff --git a/base/histogram_test.cc b/base/histogram_test.cc
new file mode 100644
index 0000000..9830392
--- /dev/null
+++ b/base/histogram_test.cc
@@ -0,0 +1,319 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test of Histogram class
+
+#include "base/histogram.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class HistogramTest : public testing::Test {
+};
+
+// Check for basic syntax and use.
+TEST(HistogramTest, StartupShutdownTest) {
+ // Try basic construction
+ Histogram histogram(L"TestHistogram", 1, 1000, 10);
+ Histogram histogram1(L"Test1Histogram", 1, 1000, 10);
+
+ LinearHistogram linear_histogram(L"TestLinearHistogram", 1, 1000, 10);
+ LinearHistogram linear_histogram1(L"Test1LinearHistogram", 1, 1000, 10);
+
+ // Use standard macros (but with fixed samples)
+ HISTOGRAM_TIMES(L"Test2Histogram", TimeDelta::FromDays(1));
+ HISTOGRAM_COUNTS(L"Test3Histogram", 30);
+
+ DHISTOGRAM_TIMES(L"Test4Histogram", TimeDelta::FromDays(1));
+ DHISTOGRAM_COUNTS(L"Test5Histogram", 30);
+
+ ASSET_HISTOGRAM_COUNTS(L"Test6Histogram", 129);
+
+ // Try to construct samples.
+ Histogram::SampleSet sample1;
+ Histogram::SampleSet sample2;
+
+ // Use copy constructor of SampleSet
+ sample1 = sample2;
+ Histogram::SampleSet sample3(sample1);
+
+ // Finally test a statistics recorder, without really using it.
+ StatisticsRecorder recorder;
+}
+
+// Repeat with a recorder present to register with.
+TEST(HistogramTest, RecordedStartupTest) {
+ // Test a statistics recorder, by letting histograms register.
+ StatisticsRecorder recorder; // This initializes the global state.
+
+ StatisticsRecorder::Histograms histograms;
+ EXPECT_EQ(0, histograms.size());
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(0, histograms.size());
+
+ // Try basic construction
+ Histogram histogram(L"TestHistogram", 1, 1000, 10);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(1, histograms.size());
+ Histogram histogram1(L"Test1Histogram", 1, 1000, 10);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(2, histograms.size());
+
+ LinearHistogram linear_histogram(L"TestLinearHistogram", 1, 1000, 10);
+ LinearHistogram linear_histogram1(L"Test1LinearHistogram", 1, 1000, 10);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(4, histograms.size());
+
+ // Use standard macros (but with fixed samples)
+ HISTOGRAM_TIMES(L"Test2Histogram", TimeDelta::FromDays(1));
+ HISTOGRAM_COUNTS(L"Test3Histogram", 30);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(6, histograms.size());
+
+ ASSET_HISTOGRAM_COUNTS(L"TestAssetHistogram", 1000);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+ EXPECT_EQ(7, histograms.size());
+
+ DHISTOGRAM_TIMES(L"Test4Histogram", TimeDelta::FromDays(1));
+ DHISTOGRAM_COUNTS(L"Test5Histogram", 30);
+ histograms.clear();
+ StatisticsRecorder::GetHistograms(&histograms); // Load up lists
+#ifndef NDEBUG
+ EXPECT_EQ(9, histograms.size());
+#else
+ EXPECT_EQ(7, histograms.size());
+#endif
+}
+
+TEST(HistogramTest, RangeTest) {
+ StatisticsRecorder recorder;
+ StatisticsRecorder::Histograms histograms;
+
+ recorder.GetHistograms(&histograms);
+ EXPECT_EQ(0, histograms.size());
+
+ Histogram histogram(L"Histogram", 1, 64, 8); // As mentioned in header file.
+ // Check that we got a nice exponential when there was enough rooom.
+ EXPECT_EQ(0, histogram.ranges(0));
+ int power_of_2 = 1;
+ for (int i = 1; i < 8; i++) {
+ EXPECT_EQ(power_of_2, histogram.ranges(i));
+ power_of_2 *= 2;
+ }
+ EXPECT_EQ(INT_MAX, histogram.ranges(8));
+
+ Histogram short_histogram(L"Histogram Shortened", 1, 7, 8);
+ // Check that when the number of buckets is short, we get a linear histogram
+ // for lack of space to do otherwise.
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i, short_histogram.ranges(i));
+ EXPECT_EQ(INT_MAX, short_histogram.ranges(8));
+
+ LinearHistogram linear_histogram(L"Linear", 1, 7, 8);
+ // We also get a nice linear set of bucket ranges when we ask for it
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i, linear_histogram.ranges(i));
+ EXPECT_EQ(INT_MAX, linear_histogram.ranges(8));
+
+ LinearHistogram linear_broad_histogram(L"Linear widened", 2, 14, 8);
+ // ...but when the list has more space, then the ranges naturally spread out.
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(2 * i, linear_broad_histogram.ranges(i));
+ EXPECT_EQ(INT_MAX, linear_broad_histogram.ranges(8));
+
+ ThreadSafeHistogram threadsafe_histogram(L"ThreadSafe", 1, 32, 15);
+ // When space is a little tight, we transition from linear to exponential.
+ // This is what happens in both the basic histogram, and the threadsafe
+ // variant (which is derived).
+ EXPECT_EQ(0, threadsafe_histogram.ranges(0));
+ EXPECT_EQ(1, threadsafe_histogram.ranges(1));
+ EXPECT_EQ(2, threadsafe_histogram.ranges(2));
+ EXPECT_EQ(3, threadsafe_histogram.ranges(3));
+ EXPECT_EQ(4, threadsafe_histogram.ranges(4));
+ EXPECT_EQ(5, threadsafe_histogram.ranges(5));
+ EXPECT_EQ(6, threadsafe_histogram.ranges(6));
+ EXPECT_EQ(7, threadsafe_histogram.ranges(7));
+ EXPECT_EQ(9, threadsafe_histogram.ranges(8));
+ EXPECT_EQ(11, threadsafe_histogram.ranges(9));
+ EXPECT_EQ(14, threadsafe_histogram.ranges(10));
+ EXPECT_EQ(17, threadsafe_histogram.ranges(11));
+ EXPECT_EQ(21, threadsafe_histogram.ranges(12));
+ EXPECT_EQ(26, threadsafe_histogram.ranges(13));
+ EXPECT_EQ(32, threadsafe_histogram.ranges(14));
+ EXPECT_EQ(INT_MAX, threadsafe_histogram.ranges(15));
+
+ recorder.GetHistograms(&histograms);
+ EXPECT_EQ(5, histograms.size());
+}
+
+// Make sure histogram handles out-of-bounds data gracefully.
+TEST(HistogramTest, BoundsTest) {
+ const int kBucketCount = 50;
+ Histogram histogram(L"Bounded", 10, 100, kBucketCount);
+
+ // Put two samples "out of bounds" above and below.
+ histogram.Add(5);
+ histogram.Add(-50);
+
+ histogram.Add(100);
+ histogram.Add(10000);
+
+ // Verify they landed in the underflow, and overflow buckets.
+ Histogram::SampleSet sample;
+ histogram.SnapshotSample(&sample);
+ EXPECT_EQ(2, sample.counts(0));
+ EXPECT_EQ(0, sample.counts(1));
+ size_t array_size = histogram.bucket_count();
+ EXPECT_EQ(kBucketCount, array_size);
+ EXPECT_EQ(0, sample.counts(array_size - 2));
+ EXPECT_EQ(2, sample.counts(array_size - 1));
+}
+
+// Check to be sure samples land as expected is "correct" buckets.
+TEST(HistogramTest, BucketPlacementTest) {
+ Histogram histogram(L"Histogram", 1, 64, 8); // As mentioned in header file.
+
+ // Check that we got a nice exponential since there was enough rooom.
+ EXPECT_EQ(0, histogram.ranges(0));
+ int power_of_2 = 1;
+ for (int i = 1; i < 8; i++) {
+ EXPECT_EQ(power_of_2, histogram.ranges(i));
+ power_of_2 *= 2;
+ }
+ EXPECT_EQ(INT_MAX, histogram.ranges(8));
+
+ // Add i+1 samples to the i'th bucket.
+ histogram.Add(0);
+ power_of_2 = 1;
+ for (int i = 1; i < 8; i++) {
+ for (int j = 0; j <= i; j++)
+ histogram.Add(power_of_2);
+ power_of_2 *= 2;
+ }
+ // Leave overflow bucket empty.
+
+ // Check to see that the bucket counts reflect our additions.
+ Histogram::SampleSet sample;
+ histogram.SnapshotSample(&sample);
+ EXPECT_EQ(INT_MAX, histogram.ranges(8));
+ for (int i = 0; i < 8; i++)
+ EXPECT_EQ(i + 1, sample.counts(i));
+}
+
+static const wchar_t* kAssetTestHistogramName = L"AssetCountTest";
+static const wchar_t* kAssetTestDebugHistogramName = L"DAssetCountTest";
+void AssetCountFunction(int sample) {
+ ASSET_HISTOGRAM_COUNTS(kAssetTestHistogramName, sample);
+ DASSET_HISTOGRAM_COUNTS(kAssetTestDebugHistogramName, sample);
+}
+// Check that asset can be added and removed from buckets.
+TEST(HistogramTest, AssetCountTest) {
+ // Start up a recorder system to identify all histograms.
+ StatisticsRecorder recorder;
+
+ // Call through the macro to instantiate the static variables.
+ AssetCountFunction(100); // Put a sample in the bucket for 100.
+
+ // Find the histogram.
+ StatisticsRecorder::Histograms histogram_list;
+ StatisticsRecorder::GetHistograms(&histogram_list);
+ ASSERT_GT(histogram_list.size(), 0u);
+ std::string ascii_name = WideToASCII(kAssetTestHistogramName);
+ std::string debug_ascii_name = WideToASCII(kAssetTestDebugHistogramName);
+ const Histogram* our_histogram = NULL;
+ const Histogram* our_debug_histogram = NULL;
+ for (StatisticsRecorder::Histograms::iterator it = histogram_list.begin();
+ it != histogram_list.end();
+ ++it) {
+ if (!(*it)->histogram_name().compare(ascii_name))
+ our_histogram = *it;
+ else if (!(*it)->histogram_name().compare(debug_ascii_name)) {
+ our_debug_histogram = *it;
+ }
+ }
+ ASSERT_TRUE(our_histogram);
+#ifndef NDEBUG
+ EXPECT_TRUE(our_debug_histogram);
+#else
+ EXPECT_FALSE(our_debug_histogram);
+#endif
+ // Verify it has a 1 in exactly one bucket (where we put the sample).
+ Histogram::SampleSet sample;
+ our_histogram->SnapshotSample(&sample);
+ int match_count = 0;
+ for (size_t i = 0; i < our_histogram->bucket_count(); ++i) {
+ if (sample.counts(i) > 0) {
+ EXPECT_LT(++match_count, 2) << "extra count in bucket " << i;
+ }
+ }
+ EXPECT_EQ(1u, match_count);
+
+ // Remove our sample.
+ AssetCountFunction(-100); // Remove a sample from the bucket for 100.
+ our_histogram->SnapshotSample(&sample); // Extract data set.
+
+ // Verify that the bucket is now empty, as are all the other buckets.
+ for (size_t i = 0; i < our_histogram->bucket_count(); ++i) {
+ EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i;
+ }
+
+ if (!our_debug_histogram)
+ return; // This is a production build.
+
+ // Repeat test with debug histogram. Note that insertion and deletion above
+ // should have cancelled each other out.
+ AssetCountFunction(100); // Add a sample into the bucket for 100.
+ our_debug_histogram->SnapshotSample(&sample);
+ match_count = 0;
+ for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) {
+ if (sample.counts(i) > 0) {
+ EXPECT_LT(++match_count, 2) << "extra count in bucket " << i;
+ }
+ }
+ EXPECT_EQ(1u, match_count);
+
+ // Remove our sample.
+ AssetCountFunction(-100); // Remove a sample from the bucket for 100.
+ our_debug_histogram->SnapshotSample(&sample); // Extract data set.
+
+ // Verify that the bucket is now empty, as are all the other buckets.
+ for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) {
+ EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i;
+ }
+}
+
+} // namespace
diff --git a/base/hmac.cc b/base/hmac.cc
new file mode 100644
index 0000000..ee86ebd
--- /dev/null
+++ b/base/hmac.cc
@@ -0,0 +1,121 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/hmac.h"
+#include "base/logging.h"
+
+HMAC::HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length)
+ : hash_alg_(hash_alg),
+ provider_(NULL),
+ hash_(NULL),
+ hkey_(NULL) {
+ if (!CryptAcquireContext(&provider_, NULL, NULL,
+ PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+ provider_ = NULL;
+ ImportKey(key, key_length);
+}
+
+HMAC::~HMAC() {
+ if (hkey_)
+ CryptDestroyKey(hkey_);
+ if (hash_)
+ CryptDestroyHash(hash_);
+ if (provider_)
+ CryptReleaseContext(provider_, 0);
+}
+
+bool HMAC::Sign(const std::string& data,
+ unsigned char* digest,
+ int digest_length) {
+ if (!provider_ || !hkey_)
+ return false;
+
+ switch (hash_alg_) {
+ case SHA1:
+ return SignWithSHA1(data, digest, digest_length);
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+void HMAC::ImportKey(const unsigned char* key, int key_length) {
+ if (key_length > kMaxKeySize) {
+ NOTREACHED();
+ return;
+ }
+
+ struct {
+ BLOBHEADER header;
+ DWORD key_size;
+ BYTE key_data[kMaxKeySize];
+ } key_blob;
+ key_blob.header.bType = PLAINTEXTKEYBLOB;
+ key_blob.header.bVersion = CUR_BLOB_VERSION;
+ key_blob.header.reserved = 0;
+ key_blob.header.aiKeyAlg = CALG_RC2;
+ key_blob.key_size = key_length;
+ memcpy(key_blob.key_data, key, key_length);
+
+ if (!CryptImportKey(provider_,
+ reinterpret_cast<const BYTE *>(&key_blob),
+ sizeof(key_blob), 0, 0, &hkey_))
+ hkey_ = NULL;
+
+ // Destroy the copy of the key.
+ SecureZeroMemory(key_blob.key_data, key_length);
+}
+
+bool HMAC::SignWithSHA1(const std::string& data,
+ unsigned char* digest,
+ int digest_length) {
+ DCHECK(provider_);
+ DCHECK(hkey_);
+
+ if (!CryptCreateHash(provider_, CALG_HMAC, hkey_, 0, &hash_))
+ return false;
+
+ HMAC_INFO hmac_info;
+ memset(&hmac_info, 0, sizeof(hmac_info));
+ hmac_info.HashAlgid = CALG_SHA1;
+ if (!CryptSetHashParam(hash_, HP_HMAC_INFO,
+ reinterpret_cast<BYTE*>(&hmac_info), 0))
+ return false;
+
+ if (!CryptHashData(hash_,
+ reinterpret_cast<const BYTE*>(data.data()),
+ static_cast<DWORD>(data.size()), 0))
+ return false;
+
+ DWORD sha1_size = digest_length;
+ if (!CryptGetHashParam(hash_, HP_HASHVAL, digest, &sha1_size, 0))
+ return false;
+
+ return true;
+}
diff --git a/base/hmac.h b/base/hmac.h
new file mode 100644
index 0000000..37c8fc9
--- /dev/null
+++ b/base/hmac.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Utility class for calculating the HMAC for a given message. We currently
+// only support SHA1 for the hash algorithm, but this can be extended easily.
+
+#ifndef BASE_HMAC_H__
+#define BASE_HMAC_H__
+
+#include <windows.h>
+#include <wincrypt.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class HMAC {
+ public:
+ // The set of supported hash functions. Extend as required.
+ enum HashAlgorithm {
+ SHA1
+ };
+
+ HMAC(HashAlgorithm hash_alg, const unsigned char* key, int key_length);
+ ~HMAC();
+
+ // Returns the HMAC in 'digest' for the message in 'data' and the key
+ // specified in the contructor.
+ bool Sign(const std::string& data, unsigned char* digest, int digest_length);
+
+ private:
+ // Import the key so that we don't have to store it ourself.
+ // TODO(paulg): Bug: http://b/1084719, 'ImportKey' will not currently work on
+ // Windows 2000 since it requires special handling for importing
+ // keys. See this link for details:
+ // http://www.derkeiler.com/Newsgroups/microsoft.public.platformsdk.security/2004-06/0270.html
+ void ImportKey(const unsigned char* key, int key_length);
+
+ // Returns the SHA1 hash of 'data' and 'key' in 'digest'. If there was any
+ // error in the calculation, this method returns false, otherwise true.
+ bool SignWithSHA1(const std::string& data,
+ unsigned char* digest,
+ int digest_length);
+
+ // Required for the SHA1 key_blob struct. We limit this to 16 bytes since
+ // Windows 2000 doesn't support keys larger than that.
+ static const int kMaxKeySize = 16;
+
+ // The hash algorithm to use.
+ HashAlgorithm hash_alg_;
+
+ // Windows Crypt API resources.
+ HCRYPTPROV provider_;
+ HCRYPTHASH hash_;
+ HCRYPTKEY hkey_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(HMAC);
+};
+
+
+#endif // BASE_HMAC_H__ \ No newline at end of file
diff --git a/base/hmac_unittest.cc b/base/hmac_unittest.cc
new file mode 100644
index 0000000..1df5137
--- /dev/null
+++ b/base/hmac_unittest.cc
@@ -0,0 +1,94 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Program to generate an HMAC-SHA1 digest for a given string, for use in
+// calculating and verifying SafeBrowsing MACs.
+
+#include <string>
+
+#include "base/hmac.h"
+#include "base/no_windows2000_unittest.h"
+
+static const int kKeySize = 16;
+static const int kDigestSize = 20;
+
+// Client key.
+const unsigned char kClientKey[kKeySize] =
+ { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd,
+ 0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 };
+
+// Expected HMAC result using kMessage and kClientKey.
+const unsigned char kReceivedHmac[kDigestSize] =
+ { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52,
+ 0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad,
+ 0x86, 0xd2, 0x48, 0x85 };
+
+const char kMessage[] =
+"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s"
+".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi"
+"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh"
+"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y"
+"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing"
+"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav"
+"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c"
+"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go"
+"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4"
+"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf"
+"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411"
+"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb"
+"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa"
+"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s."
+"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro"
+"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal"
+"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925"
+"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis"
+"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co"
+"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog"
+"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2"
+"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n";
+
+// TODO(paulg): Bug: http://b/1084719, skip this test on Windows 2000 until
+// this bug is fixed.
+class HMACTest : public NoWindows2000Test<testing::Test> {
+};
+
+TEST_F(HMACTest, HmacSafeBrowsingResponseTest) {
+ if (IsTestCaseDisabled())
+ return;
+
+ std::string message_data(kMessage);
+
+ HMAC hmac(HMAC::SHA1, kClientKey, kKeySize);
+ unsigned char calculated_hmac[kDigestSize];
+
+ EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kDigestSize));
+ EXPECT_EQ(memcmp(kReceivedHmac, calculated_hmac, kDigestSize), 0);
+}
diff --git a/base/iat_patch.cc b/base/iat_patch.cc
new file mode 100644
index 0000000..6c4fe1e
--- /dev/null
+++ b/base/iat_patch.cc
@@ -0,0 +1,246 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/iat_patch.h"
+#include "base/logging.h"
+
+namespace iat_patch {
+
+struct InterceptFunctionInformation {
+ bool finished_operation;
+ const char* imported_from_module;
+ const char* function_name;
+ void* new_function;
+ void** old_function;
+ IMAGE_THUNK_DATA** iat_thunk;
+ DWORD return_code;
+};
+
+static void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) {
+ if (NULL == iat_thunk) {
+ NOTREACHED();
+ return NULL;
+ }
+
+ // Works around the 64 bit portability warning:
+ // The Function member inside IMAGE_THUNK_DATA is really a pointer
+ // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32
+ // or IMAGE_THUNK_DATA64 for correct pointer size.
+ union FunctionThunk {
+ IMAGE_THUNK_DATA thunk;
+ void* pointer;
+ } iat_function;
+
+ iat_function.thunk = *iat_thunk;
+ return iat_function.pointer;
+}
+
+static bool InterceptEnumCallback(const PEImage &image, const char* module,
+ DWORD ordinal, const char* name, DWORD hint,
+ IMAGE_THUNK_DATA* iat, void* cookie) {
+ InterceptFunctionInformation* intercept_information =
+ reinterpret_cast<InterceptFunctionInformation*>(cookie);
+
+ if (NULL == intercept_information) {
+ NOTREACHED();
+ return false;
+ }
+
+ DCHECK(module);
+
+ if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) &&
+ (NULL != name) &&
+ (0 == lstrcmpiA(name, intercept_information->function_name))) {
+ // Save the old pointer.
+ if (NULL != intercept_information->old_function) {
+ *(intercept_information->old_function) = GetIATFunction(iat);
+ }
+
+ if (NULL != intercept_information->iat_thunk) {
+ *(intercept_information->iat_thunk) = iat;
+ }
+
+ // portability check
+ COMPILE_ASSERT(sizeof(iat->u1.Function) ==
+ sizeof(intercept_information->new_function), unknown_IAT_thunk_format);
+
+ // Patch the function.
+ intercept_information->return_code =
+ ModifyCode(&(iat->u1.Function),
+ &(intercept_information->new_function),
+ sizeof(intercept_information->new_function));
+
+ // Terminate further enumeration.
+ intercept_information->finished_operation = true;
+ return false;
+ }
+
+ return true;
+}
+
+DWORD InterceptImportedFunction(HMODULE module_handle,
+ const char* imported_from_module,
+ const char* function_name, void* new_function,
+ void** old_function,
+ IMAGE_THUNK_DATA** iat_thunk) {
+ if ((NULL == module_handle) || (NULL == imported_from_module) ||
+ (NULL == function_name) || (NULL == new_function)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ PEImage target_image(module_handle);
+ if (!target_image.VerifyMagic()) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ InterceptFunctionInformation intercept_information = {
+ false,
+ imported_from_module,
+ function_name,
+ new_function,
+ old_function,
+ iat_thunk,
+ ERROR_GEN_FAILURE};
+
+ // First go through the IAT. If we don't find the import we are looking
+ // for in IAT, search delay import table.
+ target_image.EnumAllImports(InterceptEnumCallback, &intercept_information);
+ if (!intercept_information.finished_operation) {
+ target_image.EnumAllDelayImports(InterceptEnumCallback,
+ &intercept_information);
+ }
+
+ return intercept_information.return_code;
+}
+
+DWORD RestoreImportedFunction(void* intercept_function,
+ void* original_function,
+ IMAGE_THUNK_DATA* iat_thunk) {
+ if ((NULL == intercept_function) || (NULL == original_function) ||
+ (NULL == iat_thunk)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ if (GetIATFunction(iat_thunk) != intercept_function) {
+ // Check if someone else has intercepted on top of us.
+ // We cannot unpatch in this case, just raise a red flag.
+ NOTREACHED();
+ return ERROR_INVALID_FUNCTION;
+ }
+
+ return ModifyCode(&(iat_thunk->u1.Function),
+ &original_function,
+ sizeof(original_function));
+}
+
+DWORD ModifyCode(void* old_code, void* new_code, int length) {
+ if ((NULL == old_code) || (NULL == new_code) || (0 == length)) {
+ NOTREACHED();
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ // Change the page protection so that we can write.
+ DWORD error = NO_ERROR;
+ DWORD old_page_protection = 0;
+ if (VirtualProtect(old_code,
+ length,
+ PAGE_READWRITE,
+ &old_page_protection)) {
+
+ // Write the data.
+ CopyMemory(old_code, new_code, length);
+
+ // Restore the old page protection.
+ error = ERROR_SUCCESS;
+ VirtualProtect(old_code,
+ length,
+ old_page_protection,
+ &old_page_protection);
+ } else {
+ error = GetLastError();
+ NOTREACHED();
+ }
+
+ return error;
+}
+
+IATPatchFunction::IATPatchFunction()
+ : original_function_(NULL),
+ iat_thunk_(NULL),
+ intercept_function_(NULL) {
+}
+
+IATPatchFunction::~IATPatchFunction() {
+ if (NULL != intercept_function_) {
+ DWORD error = Unpatch();
+ DCHECK_EQ(NO_ERROR, error);
+ }
+}
+
+DWORD IATPatchFunction::Patch(HMODULE module_handle,
+ const char* imported_from_module,
+ const char* function_name,
+ void* new_function) {
+ DCHECK_EQ(static_cast<void*>(NULL), original_function_);
+ DCHECK_EQ(static_cast<IMAGE_THUNK_DATA*>(NULL), iat_thunk_);
+ DCHECK_EQ(static_cast<void*>(NULL), intercept_function_);
+
+ DWORD error = InterceptImportedFunction(module_handle,
+ imported_from_module,
+ function_name,
+ new_function,
+ &original_function_,
+ &iat_thunk_);
+
+ if (NO_ERROR == error) {
+ DCHECK_NE(original_function_, intercept_function_);
+ intercept_function_ = new_function;
+ }
+
+ return error;
+}
+
+DWORD IATPatchFunction::Unpatch() {
+ DWORD error = RestoreImportedFunction(intercept_function_,
+ original_function_,
+ iat_thunk_);
+
+ if (NO_ERROR == error) {
+ intercept_function_ = NULL;
+ original_function_ = NULL;
+ iat_thunk_ = NULL;
+ }
+
+ return error;
+}
+
+} // namespace iat_patch
diff --git a/base/iat_patch.h b/base/iat_patch.h
new file mode 100644
index 0000000..91e6f99
--- /dev/null
+++ b/base/iat_patch.h
@@ -0,0 +1,140 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file declares a helpers to intercept functions from a DLL.
+//
+// This set of functions are designed to intercept functions for a
+// specific DLL imported from another DLL. This is the case when,
+// for example, we want to intercept CertDuplicateCertificateContext
+// function (exported from crypt32.dll) called by wininet.dll.
+
+#ifndef BASE_IAT_PATCH_H__
+#define BASE_IAT_PATCH_H__
+
+#include <windows.h>
+#include "base/basictypes.h"
+#include "base/pe_image.h"
+
+namespace iat_patch {
+
+// Helper to intercept a function in an import table of a specific
+// module.
+//
+// Arguments:
+// module_handle Module to be intercepted
+// imported_from_module Module that exports the symbol
+// function_name Name of the API to be intercepted
+// new_function Interceptor function
+// old_function Receives the original function pointer
+// iat_thunk Receives pointer to IAT_THUNK_DATA
+// for the API from the import table.
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+// as defined in winerror.h
+//
+DWORD InterceptImportedFunction(HMODULE module_handle,
+ const char* imported_from_module,
+ const char* function_name,
+ void* new_function,
+ void** old_function,
+ IMAGE_THUNK_DATA** iat_thunk);
+
+// Restore intercepted IAT entry with the original function.
+//
+// Arguments:
+// intercept_function Interceptor function
+// original_function Receives the original function pointer
+//
+// Returns: Returns NO_ERROR on success or Windows error code
+// as defined in winerror.h
+//
+DWORD RestoreImportedFunction(void* intercept_function,
+ void* original_function,
+ IMAGE_THUNK_DATA* iat_thunk);
+
+// Change the page protection (of code pages) to writable and copy
+// the data at the specified location
+//
+// Arguments:
+// old_code Target location to copy
+// new_code Source
+// length Number of bytes to copy
+//
+// Returns: Windows error code (winerror.h). NO_ERROR if successful
+//
+DWORD ModifyCode(void* old_code,
+ void* new_code,
+ int length);
+
+// A class that encapsulates IAT patching helpers and restores
+// the original function in the destructor.
+class IATPatchFunction {
+ public:
+ IATPatchFunction();
+ ~IATPatchFunction();
+
+ // Intercept a function in an import table of a specific
+ // module. Save the original function and the import
+ // table address. These values will be used later
+ // during Unpatch
+ //
+ // Arguments:
+ // module_handle Module to be intercepted
+ // imported_from_module Module that exports the 'function_name'
+ // function_name Name of the API to be intercepted
+ //
+ // Returns: Windows error code (winerror.h). NO_ERROR if successful
+ //
+ DWORD Patch(HMODULE module_handle,
+ const char* imported_from_module,
+ const char* function_name,
+ void* new_function);
+
+ // Unpatch the IAT entry using internally saved original
+ // function.
+ //
+ // Returns: Windows error code (winerror.h). NO_ERROR if successful
+ //
+ DWORD Unpatch();
+
+ bool is_patched() const {
+ return (NULL != intercept_function_);
+ }
+
+ private:
+ void* intercept_function_;
+ void* original_function_;
+ IMAGE_THUNK_DATA* iat_thunk_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(IATPatchFunction);
+};
+
+} // namespace iat_patch
+
+#endif // BASE_IAT_PATCH_H__
diff --git a/base/icu_util.cc b/base/icu_util.cc
new file mode 100644
index 0000000..4e72632
--- /dev/null
+++ b/base/icu_util.cc
@@ -0,0 +1,70 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <string>
+
+#include "base/icu_util.h"
+
+#include "base/logging.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "unicode/udata.h"
+
+namespace icu_util {
+
+bool Initialize() {
+ // Assert that we are not called more than once. Even though calling this
+ // function isn't harmful (ICU can handle it), being called twice probably
+ // indicates a programming error.
+#ifndef DEBUG
+ static bool called_once = false;
+ DCHECK(!called_once);
+ called_once = true;
+#endif
+
+ // We expect to find the ICU data module alongside the current module.
+ std::wstring data_path;
+ PathService::Get(base::DIR_MODULE, &data_path);
+ file_util::AppendToPath(&data_path, L"icudt38.dll");
+
+ HMODULE module = LoadLibrary(data_path.c_str());
+ if (!module)
+ return false;
+
+ FARPROC addr = GetProcAddress(module, "icudt38_dat");
+ if (!addr)
+ return false;
+
+ UErrorCode err = U_ZERO_ERROR;
+ udata_setCommonData(reinterpret_cast<void*>(addr), &err);
+ return err == U_ZERO_ERROR;
+}
+
+} // namespace icu_util
diff --git a/base/icu_util.h b/base/icu_util.h
new file mode 100644
index 0000000..d4af52c
--- /dev/null
+++ b/base/icu_util.h
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ICU_UTIL_H__
+#define BASE_ICU_UTIL_H__
+
+namespace icu_util {
+
+// Call this function to load ICU's data tables for the current process. This
+// function should be called before ICU is used.
+bool Initialize();
+
+} // namespace icu_util
+
+#endif // BASE_ICU_UTIL_H__
diff --git a/base/id_map.h b/base/id_map.h
new file mode 100644
index 0000000..e945f6a
--- /dev/null
+++ b/base/id_map.h
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_ID_MAP_H__
+#define BASE_ID_MAP_H__
+
+// hash map is common (GCC4 and Dinkumware also support it, for example), but
+// is not strictly part of the C++ standard. MS puts it into a funny namespace,
+// although most other vendors seem to use std. This may have to change if
+// other platforms are supported.
+#include <hash_map>
+using stdext::hash_map;
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// This object maintains a list of IDs that can be quickly converted to
+// pointers to objects. It is implemented as a hash table, optimized for
+// relatively small data sets (in the common case, there will be exactly one
+// item in the list).
+//
+// Items can be inserted into the container with arbitrary ID, but the caller
+// must ensure they are unique. Inserting IDs and relying on automatically
+// generated ones is not allowed because they can collide.
+template<class T>
+class IDMap {
+ private:
+ typedef hash_map<int32, T*> HashTable;
+
+ public:
+ IDMap() : next_id_(1) {
+ }
+ IDMap(const IDMap& other) : next_id_(other.next_id_),
+ data_(other.data_) {
+ }
+
+ // support const iterators over the items
+ // Note, use iterator->first to get the ID, iterator->second to get the T*
+ typedef typename HashTable::const_iterator const_iterator;
+ const_iterator begin() const {
+ return data_.begin();
+ }
+ const_iterator end() const {
+ return data_.end();
+ }
+
+ // Adds a view with an automatically generated unique ID. See AddWithID.
+ int32 Add(T* data) {
+ int32 this_id = next_id_;
+ DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item";
+ data_[this_id] = data;
+ next_id_++;
+ return this_id;
+ }
+
+ // Adds a new data member with the specified ID. The ID must not be in
+ // the list. The caller either must generate all unique IDs itself and use
+ // this function, or allow this object to generate IDs and call Add. These
+ // two methods may not be mixed, or duplicate IDs may be generated
+ void AddWithID(T* data, int32 id) {
+ DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item";
+ data_[id] = data;
+ }
+
+ void Remove(int32 id) {
+ HashTable::iterator i = data_.find(id);
+ if (i == data_.end()) {
+ NOTREACHED() << "Attempting to remove an item not in the list";
+ return;
+ }
+ data_.erase(i);
+ }
+
+ bool IsEmpty() const {
+ return data_.empty();
+ }
+
+ T* Lookup(int32 id) const {
+ HashTable::const_iterator i = data_.find(id);
+ if (i == data_.end())
+ return NULL;
+ return i->second;
+ }
+
+ size_t size() const {
+ return data_.size();
+ }
+
+ protected:
+ // The next ID that we will return from Add()
+ int32 next_id_;
+
+ HashTable data_;
+};
+
+#endif // BASE_ID_MAP_H__
diff --git a/base/idle_timer.cc b/base/idle_timer.cc
new file mode 100644
index 0000000..a69cba3
--- /dev/null
+++ b/base/idle_timer.cc
@@ -0,0 +1,103 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/idle_timer.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+
+IdleTimerTask::IdleTimerTask(TimeDelta idle_time, bool repeat)
+ : idle_interval_(idle_time),
+ repeat_(repeat),
+ get_last_input_info_fn_(GetLastInputInfo) {
+}
+
+IdleTimerTask::~IdleTimerTask() {
+ Stop();
+}
+
+void IdleTimerTask::Start() {
+ DCHECK(!timer_.get());
+ StartTimer();
+}
+
+void IdleTimerTask::Stop() {
+ timer_.reset();
+}
+
+void IdleTimerTask::Run() {
+ // Verify we can fire the idle timer.
+ if (TimeUntilIdle().InMilliseconds() <= 0) {
+ OnIdle();
+ last_time_fired_ = Time::Now();
+ }
+ Stop();
+ StartTimer(); // Restart the timer for next run.
+}
+
+void IdleTimerTask::StartTimer() {
+ DCHECK(timer_ == NULL);
+ TimeDelta delay = TimeUntilIdle();
+ if (delay.InMilliseconds() < 0)
+ delay = TimeDelta();
+ timer_.reset(new OneShotTimer(delay));
+ timer_->set_unowned_task(this);
+ timer_->Start();
+}
+
+TimeDelta IdleTimerTask::CurrentIdleTime() {
+ // TODO(mbelshe): This is windows-specific code.
+ LASTINPUTINFO info;
+ info.cbSize = sizeof(info);
+ if (get_last_input_info_fn_(&info)) {
+ // Note: GetLastInputInfo returns a 32bit value which rolls over ~49days.
+ int32 last_input_time = info.dwTime;
+ int32 current_time = GetTickCount();
+ int32 interval = current_time - last_input_time;
+ // Interval will go negative if we've been idle for 2GB of ticks.
+ if (interval < 0)
+ interval = -interval;
+ return TimeDelta::FromMilliseconds(interval);
+ }
+ NOTREACHED();
+ return TimeDelta::FromMilliseconds(0);
+}
+
+TimeDelta IdleTimerTask::TimeUntilIdle() {
+ TimeDelta time_since_last_fire = Time::Now() - last_time_fired_;
+ TimeDelta current_idle_time = CurrentIdleTime();
+ if (current_idle_time > time_since_last_fire) {
+ if (repeat_)
+ return idle_interval_ - time_since_last_fire;
+ return idle_interval_;
+ }
+ return idle_interval_ - current_idle_time;
+}
diff --git a/base/idle_timer.h b/base/idle_timer.h
new file mode 100644
index 0000000..01f2fc0
--- /dev/null
+++ b/base/idle_timer.h
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_IDLE_TIMER_H__
+#define BASE_IDLE_TIMER_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/task.h"
+#include "base/timer.h"
+
+// IdleTimer is a recurring Timer task which runs only when the system is idle.
+// System Idle time is defined as not having any user keyboard or mouse
+// activity for some period of time. Because the timer is user dependant, it
+// is possible for the timer to never fire.
+
+//
+// Usage should be for low-priority tasks, and may look like this:
+//
+// class MyIdleTimerTask : public IdleTimerTask {
+// public:
+// // This task will run after 5 seconds of idle time
+// // and not more often than once per minute.
+// MyIdleTimerTask() : IdleTimerTask(5, 60) {};
+// virtual void OnIdle() { do something };
+// }
+//
+// MyIdleTimerTask *task = new MyIdleTimerTask();
+// task->Start();
+//
+// // As with all TimerTasks, the caller must dispose the object.
+// delete task; // Will Stop the timer and cleanup the task.
+
+class TimerManager;
+
+// Function prototype for GetLastInputInfo.
+typedef BOOL (__stdcall *GetLastInputInfoFunction)(PLASTINPUTINFO plii);
+
+class IdleTimerTask : public Task {
+ public:
+ // Create an IdleTimerTask.
+ // idle_time: idle time required before this task can run.
+ // repeat: true if the timer should fire multiple times per idle,
+ // false to fire once per idle.
+ IdleTimerTask(TimeDelta idle_time, bool repeat);
+
+ // On destruction, the IdleTimerTask will Stop itself and delete the Task.
+ virtual ~IdleTimerTask();
+
+ // Start the IdleTimerTask.
+ void Start();
+
+ // Stop the IdleTimertask.
+ void Stop();
+
+ // The method to run when the timer elapses.
+ virtual void OnIdle() = 0;
+
+ protected:
+ // Override the GetLastInputInfo function.
+ void set_last_input_info_fn(GetLastInputInfoFunction function) {
+ get_last_input_info_fn_ = function;
+ }
+
+ private:
+ // This task's run method.
+ virtual void Run();
+
+ // Start the timer.
+ void StartTimer();
+
+ // Gets the number of milliseconds since the last input event.
+ TimeDelta CurrentIdleTime();
+
+ // Compute time until idle. Returns 0 if we are now idle.
+ TimeDelta TimeUntilIdle();
+
+ TimeDelta idle_interval_;
+ bool repeat_;
+ Time last_time_fired_; // The last time the idle timer fired.
+ // will be 0 until the timer fires the first time.
+ scoped_ptr<OneShotTimer> timer_;
+
+ GetLastInputInfoFunction get_last_input_info_fn_;
+};
+
+#endif // BASE_IDLE_TIMER_H__
diff --git a/base/idletimer_unittest.cc b/base/idletimer_unittest.cc
new file mode 100644
index 0000000..c3e8e6d
--- /dev/null
+++ b/base/idletimer_unittest.cc
@@ -0,0 +1,221 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/idle_timer.h"
+#include "base/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class IdleTimerTest : public testing::Test {
+ };
+};
+
+// We Mock the GetLastInputInfo function to return
+// the time stored here.
+static DWORD mock_idle_time = GetTickCount();
+
+BOOL __stdcall MockGetLastInputInfoFunction(PLASTINPUTINFO plii) {
+ DCHECK(plii->cbSize == sizeof(LASTINPUTINFO));
+ plii->dwTime = mock_idle_time;
+ return TRUE;
+}
+
+// TestIdle task fires after 100ms of idle time.
+class TestIdleTask : public IdleTimerTask {
+ public:
+ TestIdleTask(bool repeat)
+ : IdleTimerTask(TimeDelta::FromMilliseconds(100), repeat),
+ idle_counter_(0) {
+ set_last_input_info_fn(MockGetLastInputInfoFunction);
+ }
+
+ int get_idle_counter() { return idle_counter_; }
+
+ virtual void OnIdle() {
+ idle_counter_++;
+ }
+
+ private:
+ int idle_counter_;
+};
+
+// A task to help us quit the test.
+class TestFinishedTask : public Task {
+ public:
+ TestFinishedTask() {}
+ void Run() {
+ MessageLoop::current()->Quit();
+ }
+};
+
+// A timer which resets the idle clock.
+class ResetIdleTask : public Task {
+ public:
+ ResetIdleTask() {}
+ void Run() {
+ mock_idle_time = GetTickCount();
+ }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// NoRepeat tests:
+// A non-repeating idle timer will fire once on idle, and
+// then will not fire again unless it goes non-idle first.
+
+TEST(IdleTimerTest, NoRepeatIdle) {
+ // Create an IdleTimer, which should fire once after 100ms.
+ // Create a Quit timer which will fire after 1s.
+ // Verify that we fired exactly once.
+
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(false);
+ TestFinishedTask finish_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+ test_task.Start();
+ loop->Run();
+
+ EXPECT_EQ(test_task.get_idle_counter(), 1);
+ delete t;
+}
+
+TEST(IdleTimerTest, NoRepeatFlipIdleOnce) {
+ // Create an IdleTimer, which should fire once after 100ms.
+ // Create a Quit timer which will fire after 1s.
+ // Create a timer to reset once, idle after 500ms.
+ // Verify that we fired exactly twice.
+
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(false);
+ TestFinishedTask finish_task;
+ ResetIdleTask reset_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+ Timer* t2 = loop->timer_manager()->StartTimer(500, &reset_task, false);
+ test_task.Start();
+ loop->Run();
+
+ EXPECT_EQ(test_task.get_idle_counter(), 2);
+ delete t1;
+ delete t2;
+}
+
+TEST(IdleTimerTest, NoRepeatNotIdle) {
+ // Create an IdleTimer, which should fire once after 100ms.
+ // Create a Quit timer which will fire after 1s.
+ // Create a timer to reset idle every 50ms.
+ // Verify that we never fired.
+
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(false);
+ TestFinishedTask finish_task;
+ ResetIdleTask reset_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+ Timer* reset_timer = loop->timer_manager()->StartTimer(50, &reset_task, true);
+ test_task.Start();
+ loop->Run();
+ loop->timer_manager()->StopTimer(reset_timer);
+
+ EXPECT_EQ(test_task.get_idle_counter(), 0);
+ delete t;
+ delete reset_timer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Repeat tests:
+// A repeating idle timer will fire repeatedly on each interval, as long
+// as it has been idle. So, if the machine remains idle, it will continue
+// firing over and over.
+
+TEST(IdleTimerTest, Repeat) {
+ // Create an IdleTimer, which should fire repeatedly after 100ms.
+ // Create a Quit timer which will fire after 1.05s.
+ // Verify that we fired 10 times.
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(true);
+ TestFinishedTask finish_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t = loop->timer_manager()->StartTimer(1050, &finish_task, false);
+ test_task.Start();
+ loop->Run();
+
+ // In a perfect world, the idle_counter should be 10. However,
+ // since timers aren't guaranteed to fire perfectly, this can
+ // be less. Just expect more than 5 and no more than 10.
+ EXPECT_GT(test_task.get_idle_counter(), 5);
+ EXPECT_LE(test_task.get_idle_counter(), 10);
+ delete t;
+}
+
+TEST(IdleTimerTest, RepeatIdleReset) {
+ // Create an IdleTimer, which should fire repeatedly after 100ms.
+ // Create a Quit timer which will fire after 1s.
+ // Create a reset timer, which fires after 550ms
+ // Verify that we fired 9 times.
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(true);
+ ResetIdleTask reset_task;
+ TestFinishedTask finish_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+ Timer* t2 = loop->timer_manager()->StartTimer(550, &reset_task, false);
+ test_task.Start();
+ loop->Run();
+
+ // In a perfect world, the idle_counter should be 9. However,
+ // since timers aren't guaranteed to fire perfectly, this can
+ // be less. Just expect more than 5 and no more than 9.
+ EXPECT_GT(test_task.get_idle_counter(), 5);
+ EXPECT_LE(test_task.get_idle_counter(), 9);
+ delete t1;
+ delete t2;
+}
+
+TEST(IdleTimerTest, RepeatNotIdle) {
+ // Create an IdleTimer, which should fire repeatedly after 100ms.
+ // Create a Quit timer which will fire after 1s.
+ // Create a timer to reset idle every 50ms.
+ // Verify that we never fired.
+
+ mock_idle_time = GetTickCount();
+ TestIdleTask test_task(true);
+ TestFinishedTask finish_task;
+ ResetIdleTask reset_task;
+ MessageLoop* loop = MessageLoop::current();
+ Timer* t1 = loop->timer_manager()->StartTimer(1000, &finish_task, false);
+ Timer* reset_timer = loop->timer_manager()->StartTimer(50, &reset_task, true);
+ test_task.Start();
+ loop->Run();
+ loop->timer_manager()->StopTimer(reset_timer);
+
+ EXPECT_EQ(test_task.get_idle_counter(), 0);
+ delete t1;
+ delete reset_timer;
+}
diff --git a/base/image_util.cc b/base/image_util.cc
new file mode 100644
index 0000000..172a595
--- /dev/null
+++ b/base/image_util.cc
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+#include <ImageHlp.h>
+#include <psapi.h>
+
+#include "base/image_util.h"
+#include "base/process_util.h"
+
+// imagehlp.dll appears to ship in all win versions after Win95.
+// nsylvain verified it is present in win2k.
+// Using #pragma comment for dependency, instead of LoadLibrary/GetProcAddress.
+#pragma comment(lib, "imagehlp.lib")
+
+namespace image_util {
+
+// ImageMetrics
+ImageMetrics::ImageMetrics(HANDLE process) : process_(process) {
+}
+
+ImageMetrics::~ImageMetrics() {
+}
+
+bool ImageMetrics::GetDllImageSectionData(const std::string& loaded_dll_name,
+ ImageSectionsData* section_sizes) {
+ // Get a handle to the loaded DLL
+ HMODULE the_dll = GetModuleHandleA(loaded_dll_name.c_str());
+ char full_filename[MAX_PATH];
+ // Get image path
+ if (GetModuleFileNameExA(process_, the_dll, full_filename, MAX_PATH)) {
+ return GetImageSectionSizes(full_filename, section_sizes);
+ }
+ return false;
+}
+
+bool ImageMetrics::GetProcessImageSectionData(ImageSectionsData*
+ section_sizes) {
+ char exe_path[MAX_PATH];
+ // Get image path
+ if (GetModuleFileNameExA(process_, NULL, exe_path, MAX_PATH)) {
+ return GetImageSectionSizes(exe_path, section_sizes);
+ }
+ return false;
+}
+
+// private
+bool ImageMetrics::GetImageSectionSizes(char* qualified_path,
+ ImageSectionsData* result) {
+ LOADED_IMAGE li;
+ // TODO (timsteele): There is no unicode version for MapAndLoad, hence
+ // why ansi functions are used in this class. Should we try and rewrite
+ // this call ourselves to be safe?
+ if (MapAndLoad(qualified_path, 0, &li, FALSE, TRUE)) {
+ IMAGE_SECTION_HEADER* section_header = li.Sections;
+ for (unsigned i = 0; i < li.NumberOfSections; i++, section_header++) {
+ std::string name(reinterpret_cast<char*>(section_header->Name));
+ ImageSectionData data(name, section_header->Misc.VirtualSize ?
+ section_header->Misc.VirtualSize :
+ section_header->SizeOfRawData);
+ // copy into result
+ result->push_back(data);
+ }
+ } else {
+ // map and load failed
+ return false;
+ }
+ UnMapAndLoad(&li);
+ return true;
+}
+
+} // namespace image_util
diff --git a/base/image_util.h b/base/image_util.h
new file mode 100644
index 0000000..094da08
--- /dev/null
+++ b/base/image_util.h
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file/namespace contains utility functions for gathering
+// information about PE (Portable Executable) headers within
+// images (dll's / exe's )
+
+#ifndef BASE_IMAGE_UTIL_H__
+#define BASE_IMAGE_UTIL_H__
+
+#include <vector>
+#include "base/basictypes.h"
+#include <windows.h>
+
+namespace image_util {
+
+// Contains both the PE section name (.text, .reloc etc) and its size.
+struct ImageSectionData {
+ std::string name;
+ size_t size_in_bytes;
+ ImageSectionData (const std::string& section_name, size_t section_size) :
+ name (section_name), size_in_bytes(section_size) {
+ }
+};
+
+typedef std::vector<ImageSectionData> ImageSectionsData;
+
+// Provides image statistics for modules of a specified process, or for the
+// specified process' own executable file. To use, invoke CreateImageMetrics()
+// to get an instance for a specified process, then access the information via
+// methods.
+class ImageMetrics {
+ public:
+ // Creates an ImageMetrics instance for given process owned by
+ // the caller.
+ explicit ImageMetrics(HANDLE process);
+ ~ImageMetrics();
+
+ // Fills a vector of ImageSectionsData containing name/size info
+ // for every section found in the specified dll's PE section table.
+ // The DLL must be loaded by the process associated with this ImageMetrics
+ // instance.
+ bool GetDllImageSectionData(const std::string& loaded_dll_name,
+ ImageSectionsData* section_sizes);
+
+ // Fills a vector if ImageSectionsData containing name/size info
+ // for every section found in the executable file of the process
+ // associated with this ImageMetrics instance.
+ bool GetProcessImageSectionData(ImageSectionsData* section_sizes);
+
+ private:
+ // Helper for GetDllImageSectionData and GetProcessImageSectionData
+ bool GetImageSectionSizes(char* qualified_path, ImageSectionsData* result);
+
+ HANDLE process_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ImageMetrics);
+};
+
+} // namespace image_util
+
+#endif \ No newline at end of file
diff --git a/base/json_reader.cc b/base/json_reader.cc
new file mode 100644
index 0000000..1ec5f637
--- /dev/null
+++ b/base/json_reader.cc
@@ -0,0 +1,605 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/json_reader.h"
+
+#include <float.h>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/values.h"
+
+static const JSONReader::Token kInvalidToken(JSONReader::Token::INVALID_TOKEN,
+ 0, 0);
+static const int kStackLimit = 100;
+
+namespace {
+
+inline int HexToInt(wchar_t c) {
+ if ('0' <= c && c <= '9') {
+ return c - '0';
+ } else if ('A' <= c && c <= 'F') {
+ return c - 'A' + 10;
+ } else if ('a' <= c && c <= 'f') {
+ return c - 'a' + 10;
+ }
+ NOTREACHED();
+ return 0;
+}
+
+// A helper method for ParseNumberToken. It reads an int from the end of
+// token. The method returns false if there is no valid integer at the end of
+// the token.
+bool ReadInt(JSONReader::Token& token, bool can_have_leading_zeros) {
+ wchar_t first = token.NextChar();
+ int len = 0;
+
+ // Read in more digits
+ wchar_t c = first;
+ while ('\0' != c && '0' <= c && c <= '9') {
+ ++token.length;
+ ++len;
+ c = token.NextChar();
+ }
+ // We need at least 1 digit.
+ if (len == 0)
+ return false;
+
+ if (!can_have_leading_zeros && len > 1 && '0' == first)
+ return false;
+
+ return true;
+}
+
+// A helper method for ParseStringToken. It reads |digits| hex digits from the
+// token. If the sequence if digits is not valid (contains other characters),
+// the method returns false.
+bool ReadHexDigits(JSONReader::Token& token, int digits) {
+ for (int i = 1; i <= digits; ++i) {
+ wchar_t c = *(token.begin + token.length + i);
+ if ('\0' == c)
+ return false;
+ if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') ||
+ ('A' <= c && c <= 'F'))) {
+ return false;
+ }
+ }
+
+ token.length += digits;
+ return true;
+}
+
+} // anonymous namespace
+
+/* static */
+bool JSONReader::Read(const std::string& json, Value** root) {
+ return JsonToValue(json, root, true);
+}
+
+/* static */
+bool JSONReader::JsonToValue(const std::string& json, Value** root,
+ bool check_root) {
+ // Assume input is UTF8. The conversion from UTF8 to wstring removes null
+ // bytes for us (a good thing).
+ std::wstring json_wide(UTF8ToWide(json));
+ const wchar_t* json_cstr = json_wide.c_str();
+
+ // When the input JSON string starts with a UTF-8 Byte-Order-Mark
+ // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode
+ // BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from
+ // mis-treating a Unicode BOM as an invalid character and returning false,
+ // skip a converted Unicode BOM if it exists.
+ if (!json_wide.empty() && json_cstr[0] == 0xFEFF) {
+ ++json_cstr;
+ }
+
+ JSONReader reader(json_cstr);
+
+ Value* temp_root = NULL;
+ bool success = reader.BuildValue(&temp_root, check_root);
+
+ // Only modify root_ if we have valid JSON and nothing else.
+ if (success && reader.ParseToken().type == Token::END_OF_INPUT) {
+ *root = temp_root;
+ return true;
+ }
+
+ if (temp_root)
+ delete temp_root;
+ return false;
+}
+
+JSONReader::JSONReader(const wchar_t* json_start_pos)
+ : json_pos_(json_start_pos), stack_depth_(0) {}
+
+bool JSONReader::BuildValue(Value** node, bool is_root) {
+ ++stack_depth_;
+ if (stack_depth_ > kStackLimit)
+ return false;
+
+ Token token = ParseToken();
+ // The root token must be an array or an object.
+ if (is_root && token.type != Token::OBJECT_BEGIN &&
+ token.type != Token::ARRAY_BEGIN) {
+ return false;
+ }
+
+ switch (token.type) {
+ case Token::END_OF_INPUT:
+ case Token::INVALID_TOKEN:
+ return false;
+
+ case Token::NULL_TOKEN:
+ *node = Value::CreateNullValue();
+ break;
+
+ case Token::BOOL_TRUE:
+ *node = Value::CreateBooleanValue(true);
+ break;
+
+ case Token::BOOL_FALSE:
+ *node = Value::CreateBooleanValue(false);
+ break;
+
+ case Token::NUMBER:
+ if (!DecodeNumber(token, node))
+ return false;
+ break;
+
+ case Token::STRING:
+ if (!DecodeString(token, node))
+ return false;
+ break;
+
+ case Token::ARRAY_BEGIN:
+ {
+ json_pos_ += token.length;
+ token = ParseToken();
+
+ ListValue* array = new ListValue;
+ while (token.type != Token::ARRAY_END) {
+ Value* array_node = NULL;
+ if (!BuildValue(&array_node, false)) {
+ delete array;
+ return false;
+ }
+ array->Append(array_node);
+
+ // After a list value, we expect a comma or the end of the list.
+ token = ParseToken();
+ if (token.type == Token::LIST_SEPARATOR) {
+ json_pos_ += token.length;
+ token = ParseToken();
+ // Trailing commas are invalid
+ if (token.type == Token::ARRAY_END) {
+ delete array;
+ return false;
+ }
+ } else if (token.type != Token::ARRAY_END) {
+ // Unexpected value after list value. Bail out.
+ delete array;
+ return false;
+ }
+ }
+ if (token.type != Token::ARRAY_END) {
+ delete array;
+ return false;
+ }
+ *node = array;
+ break;
+ }
+
+ case Token::OBJECT_BEGIN:
+ {
+ json_pos_ += token.length;
+ token = ParseToken();
+
+ DictionaryValue* dict = new DictionaryValue;
+ while (token.type != Token::OBJECT_END) {
+ if (token.type != Token::STRING) {
+ delete dict;
+ return false;
+ }
+ Value* dict_key_value = NULL;
+ if (!DecodeString(token, &dict_key_value)) {
+ delete dict;
+ return false;
+ }
+ // Convert the key into a wstring.
+ std::wstring dict_key;
+ bool success = dict_key_value->GetAsString(&dict_key);
+ DCHECK(success);
+ delete dict_key_value;
+
+ json_pos_ += token.length;
+ token = ParseToken();
+ if (token.type != Token::OBJECT_PAIR_SEPARATOR) {
+ delete dict;
+ return false;
+ }
+
+ json_pos_ += token.length;
+ token = ParseToken();
+ Value* dict_value = NULL;
+ if (!BuildValue(&dict_value, false)) {
+ delete dict;
+ return false;
+ }
+ dict->Set(dict_key, dict_value);
+
+ // After a key/value pair, we expect a comma or the end of the
+ // object.
+ token = ParseToken();
+ if (token.type == Token::LIST_SEPARATOR) {
+ json_pos_ += token.length;
+ token = ParseToken();
+ // Trailing commas are invalid. TODO(tc): Should we allow trailing
+ // commas in objects? Seems harmless and quite convenient...
+ if (token.type == Token::OBJECT_END) {
+ delete dict;
+ return false;
+ }
+ } else if (token.type != Token::OBJECT_END) {
+ // Unexpected value after last object value. Bail out.
+ delete dict;
+ return false;
+ }
+ }
+ if (token.type != Token::OBJECT_END) {
+ delete dict;
+ return false;
+ }
+ *node = dict;
+ break;
+ }
+
+ default:
+ // We got a token that's not a value.
+ return false;
+ }
+ json_pos_ += token.length;
+
+ --stack_depth_;
+ return true;
+}
+
+JSONReader::Token JSONReader::ParseNumberToken() {
+ // We just grab the number here. We validate the size in DecodeNumber.
+ // According to RFC4627, a valid number is: [minus] int [frac] [exp]
+ Token token(Token::NUMBER, json_pos_, 0);
+ wchar_t c = *json_pos_;
+ if ('-' == c) {
+ ++token.length;
+ c = token.NextChar();
+ }
+
+ if (!ReadInt(token, false))
+ return kInvalidToken;
+
+ // Optional fraction part
+ c = token.NextChar();
+ if ('.' == c) {
+ ++token.length;
+ if (!ReadInt(token, true))
+ return kInvalidToken;
+ c = token.NextChar();
+ }
+
+ // Optional exponent part
+ if ('e' == c || 'E' == c) {
+ ++token.length;
+ c = token.NextChar();
+ if ('-' == c || '+' == c) {
+ ++token.length;
+ c = token.NextChar();
+ }
+ if (!ReadInt(token, true))
+ return kInvalidToken;
+ }
+
+ return token;
+}
+
+bool JSONReader::DecodeNumber(const Token& token, Value** node) {
+ // Determine if we want to try to parse as an int or a double.
+ bool is_double = false;
+ for (int i = 0; i < token.length; ++i) {
+ wchar_t c = *(token.begin + i);
+ if ('e' == c || 'E' == c || '.' == c) {
+ is_double = true;
+ break;
+ }
+ }
+
+ if (is_double) {
+ // Try parsing as a double.
+ double num_double;
+ int parsed_values = swscanf_s(token.begin, L"%lf", &num_double);
+ // Make sure we're not -INF, INF or NAN.
+ if (1 == parsed_values && _finite(num_double)) {
+ *node = Value::CreateRealValue(num_double);
+ return true;
+ }
+ } else {
+ int num_int;
+ int parsed_values = swscanf_s(token.begin, L"%d", &num_int);
+ if (1 == parsed_values) {
+ // Ensure the parsed value matches the string. This makes sure we don't
+ // overflow/underflow.
+ const std::wstring& back_to_str = StringPrintf(L"%d", num_int);
+ if (0 == wcsncmp(back_to_str.c_str(), token.begin,
+ back_to_str.length())) {
+ *node = Value::CreateIntegerValue(num_int);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+JSONReader::Token JSONReader::ParseStringToken() {
+ Token token(Token::STRING, json_pos_, 1);
+ wchar_t c = token.NextChar();
+ while ('\0' != c) {
+ if ('\\' == c) {
+ ++token.length;
+ c = token.NextChar();
+ // Make sure the escaped char is valid.
+ switch (c) {
+ case 'x':
+ if (!ReadHexDigits(token, 2))
+ return kInvalidToken;
+ break;
+ case 'u':
+ if (!ReadHexDigits(token, 4))
+ return kInvalidToken;
+ break;
+ case '\\':
+ case '/':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case '"':
+ break;
+ default:
+ return kInvalidToken;
+ }
+ } else if ('"' == c) {
+ ++token.length;
+ return token;
+ }
+ ++token.length;
+ c = token.NextChar();
+ }
+ return kInvalidToken;
+}
+
+bool JSONReader::DecodeString(const Token& token, Value** node) {
+ std::wstring decoded_str;
+ decoded_str.reserve(token.length - 2);
+
+ for (int i = 1; i < token.length - 1; ++i) {
+ wchar_t c = *(token.begin + i);
+ if ('\\' == c) {
+ ++i;
+ c = *(token.begin + i);
+ switch (c) {
+ case '"':
+ case '/':
+ case '\\':
+ decoded_str.push_back(c);
+ break;
+ case 'b':
+ decoded_str.push_back('\b');
+ break;
+ case 'f':
+ decoded_str.push_back('\f');
+ break;
+ case 'n':
+ decoded_str.push_back('\n');
+ break;
+ case 'r':
+ decoded_str.push_back('\r');
+ break;
+ case 't':
+ decoded_str.push_back('\t');
+ break;
+
+ case 'x':
+ decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 4) +
+ HexToInt(*(token.begin + i + 2)));
+ i += 2;
+ break;
+ case 'u':
+ decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 12 ) +
+ (HexToInt(*(token.begin + i + 2)) << 8) +
+ (HexToInt(*(token.begin + i + 3)) << 4) +
+ HexToInt(*(token.begin + i + 4)));
+ i += 4;
+ break;
+
+ default:
+ // We should only have valid strings at this point. If not,
+ // ParseStringToken didn't do it's job.
+ NOTREACHED();
+ return false;
+ }
+ } else {
+ // Not escaped
+ decoded_str.push_back(c);
+ }
+ }
+ *node = Value::CreateStringValue(decoded_str);
+
+ return true;
+}
+
+JSONReader::Token JSONReader::ParseToken() {
+ static const std::wstring kNullString(L"null");
+ static const std::wstring kTrueString(L"true");
+ static const std::wstring kFalseString(L"false");
+
+ EatWhitespaceAndComments();
+
+ Token token(Token::INVALID_TOKEN, 0, 0);
+ switch (*json_pos_) {
+ case '\0':
+ token.type = Token::END_OF_INPUT;
+ break;
+
+ case 'n':
+ if (NextStringMatch(kNullString))
+ token = Token(Token::NULL_TOKEN, json_pos_, 4);
+ break;
+
+ case 't':
+ if (NextStringMatch(kTrueString))
+ token = Token(Token::BOOL_TRUE, json_pos_, 4);
+ break;
+
+ case 'f':
+ if (NextStringMatch(kFalseString))
+ token = Token(Token::BOOL_FALSE, json_pos_, 5);
+ break;
+
+ case '[':
+ token = Token(Token::ARRAY_BEGIN, json_pos_, 1);
+ break;
+
+ case ']':
+ token = Token(Token::ARRAY_END, json_pos_, 1);
+ break;
+
+ case ',':
+ token = Token(Token::LIST_SEPARATOR, json_pos_, 1);
+ break;
+
+ case '{':
+ token = Token(Token::OBJECT_BEGIN, json_pos_, 1);
+ break;
+
+ case '}':
+ token = Token(Token::OBJECT_END, json_pos_, 1);
+ break;
+
+ case ':':
+ token = Token(Token::OBJECT_PAIR_SEPARATOR, json_pos_, 1);
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '-':
+ token = ParseNumberToken();
+ break;
+
+ case '"':
+ token = ParseStringToken();
+ break;
+ }
+ return token;
+}
+
+bool JSONReader::NextStringMatch(const std::wstring& str) {
+ for (size_t i = 0; i < str.length(); ++i) {
+ if ('\0' == *json_pos_)
+ return false;
+ if (*(json_pos_ + i) != str[i])
+ return false;
+ }
+ return true;
+}
+
+void JSONReader::EatWhitespaceAndComments() {
+ while ('\0' != *json_pos_) {
+ switch (*json_pos_) {
+ case ' ':
+ case '\n':
+ case '\r':
+ case '\t':
+ ++json_pos_;
+ break;
+ case '/':
+ // TODO(tc): This isn't in the RFC so it should be a parser flag.
+ if (!EatComment())
+ return;
+ break;
+ default:
+ // Not a whitespace char, just exit.
+ return;
+ }
+ }
+}
+
+bool JSONReader::EatComment() {
+ if ('/' != *json_pos_)
+ return false;
+
+ wchar_t next_char = *(json_pos_ + 1);
+ if ('/' == next_char) {
+ // Line comment, read until \n or \r
+ json_pos_ += 2;
+ while ('\0' != *json_pos_) {
+ switch (*json_pos_) {
+ case '\n':
+ case '\r':
+ ++json_pos_;
+ return true;
+ default:
+ ++json_pos_;
+ }
+ }
+ } else if ('*' == next_char) {
+ // Block comment, read until */
+ json_pos_ += 2;
+ while ('\0' != *json_pos_) {
+ switch (*json_pos_) {
+ case '*':
+ if ('/' == *(json_pos_ + 1)) {
+ json_pos_ += 2;
+ return true;
+ }
+ default:
+ ++json_pos_;
+ }
+ }
+ } else {
+ return false;
+ }
+ return true;
+}
diff --git a/base/json_reader.h b/base/json_reader.h
new file mode 100644
index 0000000..97f68fd
--- /dev/null
+++ b/base/json_reader.h
@@ -0,0 +1,165 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A JSON parser. Converts strings of JSON into a Value object (see
+// base/values.h).
+// http://www.ietf.org/rfc/rfc4627.txt?number=4627
+//
+// Known limitations/deviations from the RFC:
+// - Only knows how to parse ints within the range of a signed 32 bit int and
+// decimal numbers within a double.
+// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16
+// (BE or LE) and UTF-32 (BE or LE) as well.
+// - We limit nesting to 100 levels to prevent stack overflow (this is allowed
+// by the RFC).
+// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data
+// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input
+// UTF-8 string for the JSONReader::JsonToValue() function may start with a
+// UTF-8 BOM (0xEF, 0xBB, 0xBF).
+// To avoid the function from mis-treating a UTF-8 BOM as an invalid
+// character, the function skips a Unicode BOM at the beginning of the
+// Unicode string (converted from the input UTF-8 string) before parsing it.
+//
+// TODO(tc): It would be nice to give back an error string when we fail to parse JSON.
+// Parsing options:
+// - Relax trailing commas in arrays and objects
+// - Relax object keys being wrapped in double quotes
+// - Disable comment stripping
+
+#ifndef CHROME_COMMON_JSON_READER_H__
+#define CHROME_COMMON_JSON_READER_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class Value;
+
+class JSONReader {
+ public:
+ // A struct to hold a JS token.
+ class Token {
+ public:
+ enum Type {
+ OBJECT_BEGIN, // {
+ OBJECT_END, // }
+ ARRAY_BEGIN, // [
+ ARRAY_END, // ]
+ STRING,
+ NUMBER,
+ BOOL_TRUE, // true
+ BOOL_FALSE, // false
+ NULL_TOKEN, // null
+ LIST_SEPARATOR, // ,
+ OBJECT_PAIR_SEPARATOR, // :
+ END_OF_INPUT,
+ INVALID_TOKEN,
+ };
+ Token(Type t, const wchar_t* b, int len)
+ : type(t), begin(b), length(len) {}
+
+ Type type;
+
+ // A pointer into JSONReader::json_pos_ that's the beginning of this token.
+ const wchar_t* begin;
+
+ // End should be one char past the end of the token.
+ int length;
+
+ // Get the character that's one past the end of this token.
+ wchar_t NextChar() {
+ return *(begin + length);
+ }
+ };
+
+ // Reads and parses |json| and populates |root|. If |json| is not a
+ // properly formed JSON string, returns false and leaves root unaltered.
+ static bool Read(const std::string& json, Value** root);
+
+ private:
+ JSONReader(const wchar_t* json_start_pos);
+ DISALLOW_EVIL_CONSTRUCTORS(JSONReader);
+
+ FRIEND_TEST(JSONReaderTest, Reading);
+
+ // Pass through method from JSONReader::Read. We have this so unittests can
+ // disable the root check.
+ static bool JsonToValue(const std::string& json, Value** root,
+ bool check_root);
+
+ // Recursively build Value. Returns false if we don't have a valid JSON
+ // string. If |is_root| is true, we verify that the root element is either
+ // an object or an array.
+ bool BuildValue(Value** root, bool is_root);
+
+ // Parses a sequence of characters into a Token::NUMBER. If the sequence of
+ // characters is not a valid number, returns a Token::INVALID_TOKEN. Note
+ // that DecodeNumber is used to actually convert from a string to an
+ // int/double.
+ Token ParseNumberToken();
+
+ // Try and convert the substring that token holds into an int or a double. If
+ // we can (ie., no overflow), return true and create the appropriate value
+ // for |node|. Return false if we can't do the conversion.
+ bool DecodeNumber(const Token& token, Value** node);
+
+ // Parses a sequence of characters into a Token::STRING. If the sequence of
+ // characters is not a valid string, returns a Token::INVALID_TOKEN. Note
+ // that DecodeString is used to actually decode the escaped string into an
+ // actual wstring.
+ Token ParseStringToken();
+
+ // Convert the substring into a value string. This should always succeed
+ // (otherwise ParseStringToken would have failed), but returns a success bool
+ // just in case.
+ bool DecodeString(const Token& token, Value** node);
+
+ // Grabs the next token in the JSON stream. This does not increment the
+ // stream so it can be used to look ahead at the next token.
+ Token ParseToken();
+
+ // Increments json_pos_ past leading whitespace and comments.
+ void EatWhitespaceAndComments();
+
+ // If json_pos_ is at the start of a comment, eat it, otherwise, returns
+ // false.
+ bool EatComment();
+
+ // Checks if json_pos_ matches str.
+ bool NextStringMatch(const std::wstring& str);
+
+ // Pointer to the current position in the input string.
+ const wchar_t* json_pos_;
+
+ // Used to keep track of how many nested lists/dicts there are.
+ int stack_depth_;
+};
+
+#endif // CHROME_COMMON_JSON_READER_H__
diff --git a/base/json_reader_unittest.cc b/base/json_reader_unittest.cc
new file mode 100644
index 0000000..f218531
--- /dev/null
+++ b/base/json_reader_unittest.cc
@@ -0,0 +1,412 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/json_reader.h"
+#include "base/values.h"
+
+TEST(JSONReaderTest, Reading) {
+ // some whitespace checking
+ Value* root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue(" null ", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_NULL));
+ delete root;
+
+ // Invalid JSON string
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("nu", &root, false));
+ ASSERT_FALSE(root);
+
+ // Simple bool
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("true ", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_BOOLEAN));
+ delete root;
+
+ // Test number formats
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("43", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
+ int int_val = 0;
+ ASSERT_TRUE(root->GetAsInteger(&int_val));
+ ASSERT_EQ(43, int_val);
+ delete root;
+
+ // According to RFC4627, oct, hex, and leading zeros are invalid JSON.
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("043", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("0x43", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("00", &root, false));
+ ASSERT_FALSE(root);
+
+ // Test 0 (which needs to be special cased because of the leading zero
+ // clause).
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("0", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER));
+ int_val = 1;
+ ASSERT_TRUE(root->GetAsInteger(&int_val));
+ ASSERT_EQ(0, int_val);
+ delete root;
+
+ // Numbers that overflow ints should fail
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("2147483648", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("-2147483649", &root, false));
+ ASSERT_FALSE(root);
+
+ // Parse a double
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("43.1", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ double real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(43.1, real_val);
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("4.3e-1", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(.43, real_val);
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("2.1e0", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(2.1, real_val);
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("2.1e+0001", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(21.0, real_val);
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("0.01", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(0.01, real_val);
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("1.00", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_REAL));
+ real_val = 0.0;
+ ASSERT_TRUE(root->GetAsReal(&real_val));
+ ASSERT_DOUBLE_EQ(1.0, real_val);
+ delete root;
+
+ // Fractional parts must have a digit before and after the decimal point.
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1.", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue(".1", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1.e10", &root, false));
+ ASSERT_FALSE(root);
+
+ // Exponent must have a digit following the 'e'.
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1e", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1E", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1e1.", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1e1.0", &root, false));
+ ASSERT_FALSE(root);
+
+ // INF/-INF/NaN are not valid
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("1e1000", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("-1e1000", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("NaN", &root, false));
+ ASSERT_FALSE(root);
+
+ // Invalid number formats
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("4.3.1", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("4e3.1", &root, false));
+ ASSERT_FALSE(root);
+
+ // Test string parser
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("\"hello world\"", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+ std::wstring str_val;
+ ASSERT_TRUE(root->GetAsString(&str_val));
+ ASSERT_EQ(L"hello world", str_val);
+ delete root;
+
+ // Empty string
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("\"\"", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ ASSERT_TRUE(root->GetAsString(&str_val));
+ ASSERT_EQ(L"", str_val);
+ delete root;
+
+ // Test basic string escapes
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\"", &root,
+ false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ ASSERT_TRUE(root->GetAsString(&str_val));
+ ASSERT_EQ(L" \"\\/\b\f\n\r\t", str_val);
+ delete root;
+
+ // Test hex and unicode escapes including the null character.
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("\"\\x41\\x00\\u1234\"", &root, false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ ASSERT_TRUE(root->GetAsString(&str_val));
+ ASSERT_EQ(std::wstring(L"A\0\x1234", 3), str_val);
+ delete root;
+
+ // Test invalid strings
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("\"no closing quote", &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("\"\\z invalid escape char\"", &root,
+ false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("\"\\xAQ invalid hex code\"", &root,
+ false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("not enough hex chars\\x1\"", &root,
+ false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("\"not enough escape chars\\u123\"",
+ &root, false));
+ ASSERT_FALSE(root);
+ root = NULL;
+ ASSERT_FALSE(JSONReader::JsonToValue("\"extra backslash at end of input\\\"",
+ &root, false));
+ ASSERT_FALSE(root);
+
+ // Basic array
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read("[true, false, null]", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+ ListValue* list = static_cast<ListValue*>(root);
+ ASSERT_EQ(3, list->GetSize());
+ delete root;
+
+ // Empty array
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read("[]", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root);
+ ASSERT_EQ(0, list->GetSize());
+ delete root;
+
+ // Nested arrays
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read("[[true], [], [false, [], [null]], null]", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root);
+ ASSERT_EQ(4, list->GetSize());
+ delete root;
+
+ // Invalid, missing close brace.
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("[[true], [], [false, [], [null]], null", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, too many commas
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("[true,, null]", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, no commas
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("[true null]", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, trailing comma
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("[true,]", &root));
+ ASSERT_FALSE(root);
+
+ // Test objects
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read("{}", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ delete root;
+
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read(
+ "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ DictionaryValue* dict_val = static_cast<DictionaryValue*>(root);
+ real_val = 0.0;
+ ASSERT_TRUE(dict_val->GetReal(L"number", &real_val));
+ ASSERT_DOUBLE_EQ(9.87654321, real_val);
+ Value* null_val = NULL;
+ ASSERT_TRUE(dict_val->Get(L"null", &null_val));
+ ASSERT_TRUE(null_val->IsType(Value::TYPE_NULL));
+ str_val.clear();
+ ASSERT_TRUE(dict_val->GetString(L"S", &str_val));
+ ASSERT_EQ(L"str", str_val);
+ delete root;
+
+ // Test nesting
+ root = NULL;
+ ASSERT_TRUE(JSONReader::Read(
+ "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}", &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY));
+ dict_val = static_cast<DictionaryValue*>(root);
+ DictionaryValue* inner_dict = NULL;
+ ASSERT_TRUE(dict_val->GetDictionary(L"inner", &inner_dict));
+ ListValue* inner_array = NULL;
+ ASSERT_TRUE(inner_dict->GetList(L"array", &inner_array));
+ ASSERT_EQ(1, inner_array->GetSize());
+ bool bool_value = true;
+ ASSERT_TRUE(dict_val->GetBoolean(L"false", &bool_value));
+ ASSERT_FALSE(bool_value);
+ inner_dict = NULL;
+ ASSERT_TRUE(dict_val->GetDictionary(L"d", &inner_dict));
+ delete root;
+
+ // Invalid, no closing brace
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("{\"a\": true", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, keys must be quoted
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("{foo:true}", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, trailing comma
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("{\"a\":true,}", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, too many commas
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("{\"a\":true,,\"b\":false}", &root));
+ ASSERT_FALSE(root);
+
+ // Invalid, no separator
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("{\"a\" \"b\"}", &root));
+ ASSERT_FALSE(root);
+
+ // Test stack overflow
+ root = NULL;
+ std::string evil(1000000, '[');
+ evil.append(std::string(1000000, ']'));
+ ASSERT_FALSE(JSONReader::Read(evil, &root));
+ ASSERT_FALSE(root);
+
+ // A few thousand adjacent lists is fine.
+ std::string not_evil("[");
+ not_evil.reserve(15010);
+ for (int i = 0; i < 5000; ++i) {
+ not_evil.append("[],");
+ }
+ not_evil.append("[]]");
+ ASSERT_TRUE(JSONReader::Read(not_evil, &root));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_LIST));
+ list = static_cast<ListValue*>(root);
+ ASSERT_EQ(5001, list->GetSize());
+ delete root;
+
+ // Test utf8 encoded input
+ root = NULL;
+ ASSERT_TRUE(JSONReader::JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"", &root,
+ false));
+ ASSERT_TRUE(root);
+ ASSERT_TRUE(root->IsType(Value::TYPE_STRING));
+ str_val.clear();
+ ASSERT_TRUE(root->GetAsString(&str_val));
+ ASSERT_EQ(L"\x7f51\x9875", str_val);
+ delete root;
+
+ // Test invalid root objects.
+ root = NULL;
+ ASSERT_FALSE(JSONReader::Read("null", &root));
+ ASSERT_FALSE(JSONReader::Read("true", &root));
+ ASSERT_FALSE(JSONReader::Read("10", &root));
+ ASSERT_FALSE(JSONReader::Read("\"root\"", &root));
+}
diff --git a/base/json_writer.cc b/base/json_writer.cc
new file mode 100644
index 0000000..512e0a1
--- /dev/null
+++ b/base/json_writer.cc
@@ -0,0 +1,191 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/json_writer.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/values.h"
+#include "base/string_escape.h"
+
+const char kPrettyPrintLineEnding[] = "\r\n";
+
+/* static */
+void JSONWriter::Write(const Value* const node, bool pretty_print,
+ std::string* json) {
+ json->clear();
+ // Is there a better way to estimate the size of the output?
+ json->reserve(1024);
+ JSONWriter writer(pretty_print, json);
+ writer.BuildJSONString(node, 0);
+ if (pretty_print)
+ json->append(kPrettyPrintLineEnding);
+}
+
+JSONWriter::JSONWriter(bool pretty_print, std::string* json)
+ : pretty_print_(pretty_print),
+ json_string_(json) {
+ DCHECK(json);
+}
+
+void JSONWriter::BuildJSONString(const Value* const node, int depth) {
+ switch(node->GetType()) {
+ case Value::TYPE_NULL:
+ json_string_->append("null");
+ break;
+
+ case Value::TYPE_BOOLEAN:
+ {
+ bool value;
+ bool result = node->GetAsBoolean(&value);
+ DCHECK(result);
+ json_string_->append(value ? "true" : "false");
+ break;
+ }
+
+ case Value::TYPE_INTEGER:
+ {
+ int value;
+ bool result = node->GetAsInteger(&value);
+ DCHECK(result);
+ StringAppendF(json_string_, "%d", value);
+ break;
+ }
+
+ case Value::TYPE_REAL:
+ {
+ double value;
+ bool result = node->GetAsReal(&value);
+ DCHECK(result);
+ std::string real = StringPrintf("%g", value);
+ // Ensure that the number has a .0 if there's no decimal or 'e'. This
+ // makes sure that when we read the JSON back, it's interpreted as a
+ // real rather than an int.
+ if (real.find('.') == std::string::npos &&
+ real.find('e') == std::string::npos &&
+ real.find('E') == std::string::npos) {
+ real.append(".0");
+ }
+ json_string_->append(real);
+ break;
+ }
+
+ case Value::TYPE_STRING:
+ {
+ std::wstring value;
+ bool result = node->GetAsString(&value);
+ DCHECK(result);
+ AppendQuotedString(value);
+ break;
+ }
+
+ case Value::TYPE_LIST:
+ {
+ json_string_->append("[");
+ if (pretty_print_)
+ json_string_->append(" ");
+
+ const ListValue* list = static_cast<const ListValue*>(node);
+ for (size_t i = 0; i < list->GetSize(); ++i) {
+ if (i != 0) {
+ json_string_->append(",");
+ if (pretty_print_)
+ json_string_->append(" ");
+ }
+
+ Value* value = NULL;
+ bool result = list->Get(i, &value);
+ DCHECK(result);
+ BuildJSONString(value, depth);
+ }
+
+ if (pretty_print_)
+ json_string_->append(" ");
+ json_string_->append("]");
+ break;
+ }
+
+ case Value::TYPE_DICTIONARY:
+ {
+ json_string_->append("{");
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+
+ const DictionaryValue* dict =
+ static_cast<const DictionaryValue*>(node);
+ for (DictionaryValue::key_iterator key_itr = dict->begin_keys();
+ key_itr != dict->end_keys();
+ ++key_itr) {
+
+ if (key_itr != dict->begin_keys()) {
+ json_string_->append(",");
+ if (pretty_print_)
+ json_string_->append(kPrettyPrintLineEnding);
+ }
+
+ Value* value = NULL;
+ bool result = dict->Get(*key_itr, &value);
+ DCHECK(result);
+
+ if (pretty_print_)
+ IndentLine(depth + 1);
+ AppendQuotedString(*key_itr);
+ if (pretty_print_) {
+ json_string_->append(": ");
+ } else {
+ json_string_->append(":");
+ }
+ BuildJSONString(value, depth + 1);
+ }
+
+ if (pretty_print_) {
+ json_string_->append(kPrettyPrintLineEnding);
+ IndentLine(depth);
+ json_string_->append("}");
+ } else {
+ json_string_->append("}");
+ }
+ break;
+ }
+
+ default:
+ // TODO(jhughes): handle TYPE_BINARY
+ NOTREACHED() << "unknown json type";
+ }
+}
+
+void JSONWriter::AppendQuotedString(const std::wstring& str) {
+ string_escape::JavascriptDoubleQuote(str, true, json_string_);
+}
+
+void JSONWriter::IndentLine(int depth) {
+ // It may be faster to keep an indent string so we don't have to keep
+ // reallocating.
+ json_string_->append(std::string(depth * 3, ' '));
+}
diff --git a/base/json_writer.h b/base/json_writer.h
new file mode 100644
index 0000000..9f8f8d3
--- /dev/null
+++ b/base/json_writer.h
@@ -0,0 +1,71 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CHROME_COMMON_JSON_WRITER_H__
+#define CHROME_COMMON_JSON_WRITER_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+
+class Value;
+
+class JSONWriter {
+ public:
+ // Given a root node, generates a JSON string and puts it into |json|.
+ // If |pretty_print| is true, return a slightly nicer formated json string
+ // (pads with whitespace to help readability). If |pretty_print| is false,
+ // we try to generate as compact a string as possible.
+ // TODO(tc): Should we generate json if it would be invalid json (e.g.,
+ // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float
+ // values)?
+ static void Write(const Value* const node, bool pretty_print,
+ std::string* json);
+
+ private:
+ JSONWriter(bool pretty_print, std::string* json);
+ DISALLOW_EVIL_CONSTRUCTORS(JSONWriter);
+
+ // Called recursively to build the JSON string. Whe completed, value is
+ // json_string_ will contain the JSON.
+ void BuildJSONString(const Value* const node, int depth);
+
+ // Appends a quoted, escaped, version of str to json_string_.
+ void AppendQuotedString(const std::wstring& str);
+
+ // Adds space to json_string_ for the indent level.
+ void IndentLine(int depth);
+
+ // Where we write JSON data as we generate it.
+ std::string* json_string_;
+
+ bool pretty_print_;
+};
+
+#endif // CHROME_COMMON_JSON_WRITER_H__
diff --git a/base/json_writer_unittest.cc b/base/json_writer_unittest.cc
new file mode 100644
index 0000000..4cbe2d0
--- /dev/null
+++ b/base/json_writer_unittest.cc
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/json_writer.h"
+#include "base/values.h"
+
+TEST(JSONWriterTest, Writing) {
+ // Test null
+ Value* root = Value::CreateNullValue();
+ std::string output_js;
+ JSONWriter::Write(root, false, &output_js);
+ ASSERT_EQ("null", output_js);
+ delete root;
+
+ // Test empty dict
+ root = new DictionaryValue;
+ JSONWriter::Write(root, false, &output_js);
+ ASSERT_EQ("{}", output_js);
+ delete root;
+
+ // Test empty list
+ root = new ListValue;
+ JSONWriter::Write(root, false, &output_js);
+ ASSERT_EQ("[]", output_js);
+ delete root;
+
+ // Test Real values should always have a decimal or an 'e'.
+ root = Value::CreateRealValue(1.0);
+ JSONWriter::Write(root, false, &output_js);
+ ASSERT_EQ("1.0", output_js);
+ delete root;
+
+ // Writer unittests like empty list/dict nesting,
+ // list list nesting, etc.
+ DictionaryValue root_dict;
+ ListValue* list = new ListValue;
+ root_dict.Set(L"list", list);
+ DictionaryValue* inner_dict = new DictionaryValue;
+ list->Append(inner_dict);
+ inner_dict->SetInteger(L"inner int", 10);
+ ListValue* inner_list = new ListValue;
+ list->Append(inner_list);
+ list->Append(Value::CreateBooleanValue(true));
+
+ JSONWriter::Write(&root_dict, false, &output_js);
+ ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js);
+ JSONWriter::Write(&root_dict, true, &output_js);
+ ASSERT_EQ("{\r\n"
+ " \"list\": [ {\r\n"
+ " \"inner int\": 10\r\n"
+ " }, [ ], true ]\r\n"
+ "}\r\n",
+ output_js);
+}
+
diff --git a/base/linked_ptr.h b/base/linked_ptr.h
new file mode 100644
index 0000000..9204d54
--- /dev/null
+++ b/base/linked_ptr.h
@@ -0,0 +1,200 @@
+// linked_ptr.h
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A "smart" pointer type with reference tracking. Every pointer to a
+// particular object is kept on a circular linked list. When the last pointer
+// to an object is destroyed or reassigned, the object is deleted.
+//
+// Used properly, this deletes the object when the last reference goes away.
+// There are several caveats:
+// - Like all reference counting schemes, cycles lead to leaks.
+// - Each smart pointer is actually two pointers (8 bytes instead of 4).
+// - Every time a pointer is released, the entire list of pointers to that
+// object is traversed. This class is therefore NOT SUITABLE when there
+// will often be more than two or three pointers to a particular object.
+// - References are only tracked as long as linked_ptr<> objects are copied.
+// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS
+// will happen (double deletion).
+//
+// A good use of this class is storing object references in STL containers.
+// You can safely put linked_ptr<> in a vector<>.
+// Other uses may not be as good.
+//
+// Note: If you use an incomplete type with linked_ptr<>, the class
+// *containing* linked_ptr<> must have a constructor and destructor (even
+// if they do nothing!).
+//
+// Thread Safety:
+// A linked_ptr is NOT thread safe. Copying a linked_ptr object is
+// effectively a read-write operation.
+//
+// Alternative: to linked_ptr is shared_ptr, which
+// - is also two pointers in size (8 bytes for 32 bit addresses)
+// - is thread safe for copying and deletion
+// - supports weak_ptrs
+
+#ifndef BASE_LINKED_PTR_H_
+#define BASE_LINKED_PTR_H_
+
+#include "base/logging.h" // for CHECK macros
+
+// This is used internally by all instances of linked_ptr<>. It needs to be
+// a non-template class because different types of linked_ptr<> can refer to
+// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
+// So, it needs to be possible for different types of linked_ptr to participate
+// in the same circular linked list, so we need a single class type here.
+//
+// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr<T>.
+class linked_ptr_internal {
+ public:
+ // Create a new circle that includes only this instance.
+ void join_new() {
+ next_ = this;
+ }
+
+ // Join an existing circle.
+ void join(linked_ptr_internal const* ptr) {
+ next_ = ptr->next_;
+ ptr->next_ = this;
+ }
+
+ // Leave whatever circle we're part of. Returns true iff we were the
+ // last member of the circle. Once this is done, you can join() another.
+ bool depart() {
+ if (next_ == this) return true;
+ linked_ptr_internal const* p = next_;
+ while (p->next_ != this) p = p->next_;
+ p->next_ = next_;
+ return false;
+ }
+
+ private:
+ mutable linked_ptr_internal const* next_;
+};
+
+template <typename T>
+class linked_ptr {
+ public:
+ typedef T element_type;
+
+ // Take over ownership of a raw pointer. This should happen as soon as
+ // possible after the object is created.
+ explicit linked_ptr(T* ptr = NULL) { capture(ptr); }
+ ~linked_ptr() { depart(); }
+
+ // Copy an existing linked_ptr<>, adding ourselves to the list of references.
+ template <typename U> linked_ptr(linked_ptr<U> const& ptr) { copy(&ptr); }
+ linked_ptr(linked_ptr const& ptr) { DCHECK_NE(&ptr, this); copy(&ptr); }
+
+ // Assignment releases the old value and acquires the new.
+ template <typename U> linked_ptr& operator=(linked_ptr<U> const& ptr) {
+ depart();
+ copy(&ptr);
+ return *this;
+ }
+
+ linked_ptr& operator=(linked_ptr const& ptr) {
+ if (&ptr != this) {
+ depart();
+ copy(&ptr);
+ }
+ return *this;
+ }
+
+ // Smart pointer members.
+ void reset(T* ptr = NULL) { depart(); capture(ptr); }
+ T* get() const { return value_; }
+ T* operator->() const { return value_; }
+ T& operator*() const { return *value_; }
+ // Release ownership of the pointed object and returns it.
+ // Sole ownership by this linked_ptr object is required.
+ T* release() {
+ bool last = link_.depart();
+ CHECK(last);
+ T* v = value_;
+ value_ = NULL;
+ return v;
+ }
+
+ bool operator==(T* p) const { return value_ == p; }
+ bool operator!=(T* p) const { return value_ != p; }
+ template <typename U>
+ bool operator==(linked_ptr<U> const& ptr) const {
+ return value_ == ptr.get();
+ }
+ template <typename U>
+ bool operator!=(linked_ptr<U> const& ptr) const {
+ return value_ != ptr.get();
+ }
+
+ private:
+ template <typename U>
+ friend class linked_ptr;
+
+ T* value_;
+ linked_ptr_internal link_;
+
+ void depart() {
+ if (link_.depart()) delete value_;
+ }
+
+ void capture(T* ptr) {
+ value_ = ptr;
+ link_.join_new();
+ }
+
+ template <typename U> void copy(linked_ptr<U> const* ptr) {
+ value_ = ptr->get();
+ if (value_)
+ link_.join(&ptr->link_);
+ else
+ link_.join_new();
+ }
+};
+
+template<typename T> inline
+bool operator==(T* ptr, const linked_ptr<T>& x) {
+ return ptr == x.get();
+}
+
+template<typename T> inline
+bool operator!=(T* ptr, const linked_ptr<T>& x) {
+ return ptr != x.get();
+}
+
+// A function to convert T* into linked_ptr<T>
+// Doing e.g. make_linked_ptr(new FooBarBaz<type>(arg)) is a shorter notation
+// for linked_ptr<FooBarBaz<type> >(new FooBarBaz<type>(arg))
+template <typename T>
+linked_ptr<T> make_linked_ptr(T* ptr) {
+ return linked_ptr<T>(ptr);
+}
+
+#endif // BASE_LINKED_PTR_H_
diff --git a/base/linked_ptr_unittest.cc b/base/linked_ptr_unittest.cc
new file mode 100644
index 0000000..c749ce9
--- /dev/null
+++ b/base/linked_ptr_unittest.cc
@@ -0,0 +1,136 @@
+// linked_ptr_unittest.cc
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+#include <iostream>
+
+#include "base/linked_ptr.h"
+
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+int num = 0;
+
+std::string history;
+
+// Class which tracks allocation/deallocation
+struct A {
+ A(): mynum(num++) { history += StringPrintf("A%d ctor\n", mynum); }
+ virtual ~A() { history += StringPrintf("A%d dtor\n", mynum); }
+ virtual void Use() { history += StringPrintf("A%d use\n", mynum); }
+ int mynum;
+};
+
+// Subclass
+struct B: public A {
+ B() { history += StringPrintf("B%d ctor\n", mynum); }
+ ~B() { history += StringPrintf("B%d dtor\n", mynum); }
+ virtual void Use() { history += StringPrintf("B%d use\n", mynum); }
+};
+
+} // namespace
+
+TEST(LinkedPtrTest, Test) {
+ {
+ linked_ptr<A> a0, a1, a2;
+ a0 = a0;
+ a1 = a2;
+ ASSERT_EQ(a0.get(), static_cast<A*>(NULL));
+ ASSERT_EQ(a1.get(), static_cast<A*>(NULL));
+ ASSERT_EQ(a2.get(), static_cast<A*>(NULL));
+ ASSERT_TRUE(a0 == NULL);
+ ASSERT_TRUE(a1 == NULL);
+ ASSERT_TRUE(a2 == NULL);
+
+ {
+ linked_ptr<A> a3(new A);
+ a0 = a3;
+ ASSERT_TRUE(a0 == a3);
+ ASSERT_TRUE(a0 != NULL);
+ ASSERT_TRUE(a0.get() == a3);
+ ASSERT_TRUE(a0 == a3.get());
+ linked_ptr<A> a4(a0);
+ a1 = a4;
+ linked_ptr<A> a5(new A);
+ ASSERT_TRUE(a5.get() != a3);
+ ASSERT_TRUE(a5 != a3.get());
+ a2 = a5;
+ linked_ptr<B> b0(new B);
+ linked_ptr<A> a6(b0);
+ ASSERT_TRUE(b0 == a6);
+ ASSERT_TRUE(a6 == b0);
+ ASSERT_TRUE(b0 != NULL);
+ a5 = b0;
+ a5 = b0;
+ a3->Use();
+ a4->Use();
+ a5->Use();
+ a6->Use();
+ b0->Use();
+ (*b0).Use();
+ b0.get()->Use();
+ }
+
+ a0->Use();
+ a1->Use();
+ a2->Use();
+
+ a1 = a2;
+ a2.reset(new A);
+ a0.reset();
+
+ linked_ptr<A> a7;
+ }
+
+ ASSERT_EQ(history,
+ "A0 ctor\n"
+ "A1 ctor\n"
+ "A2 ctor\n"
+ "B2 ctor\n"
+ "A0 use\n"
+ "A0 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 use\n"
+ "B2 dtor\n"
+ "A2 dtor\n"
+ "A0 use\n"
+ "A0 use\n"
+ "A1 use\n"
+ "A3 ctor\n"
+ "A0 dtor\n"
+ "A3 dtor\n"
+ "A1 dtor\n"
+ );
+}
diff --git a/base/lock.cc b/base/lock.cc
new file mode 100644
index 0000000..afa1a6f
--- /dev/null
+++ b/base/lock.cc
@@ -0,0 +1,156 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Provide place to put profiling methods for the
+// Lock class.
+// The Lock class is used everywhere, and hence any changes
+// to lock.h tend to require a complete rebuild. To facilitate
+// profiler development, all the profiling methods are listed
+// here. Note that they are only instantiated in a debug
+// build, and the header provides all the trivial implementations
+// in a production build.
+
+#include "base/lock.h"
+#include "base/logging.h"
+
+Lock::Lock()
+ : lock_()
+ , recursion_count_shadow_(0) {
+#ifndef NDEBUG
+ recursion_used_ = false;
+ acquisition_count_ = 0;
+ contention_count_ = 0;
+#endif
+}
+
+Lock::~Lock() {
+#ifndef NDEBUG
+ // There should be no one to contend for the lock,
+ // ...but we need the memory barrier to get a good value.
+ lock_.Lock();
+ int final_recursion_count = recursion_count_shadow_;
+ lock_.Unlock();
+#endif
+
+ // Allow unit test exception only at end of method.
+#ifndef NDEBUG
+ DCHECK(0 == final_recursion_count);
+#endif
+}
+
+void Lock::Acquire() {
+#ifdef NDEBUG
+ lock_.Lock();
+ recursion_count_shadow_++;
+#else // NDEBUG
+ if (!lock_.Try()) {
+ // We have contention.
+ lock_.Lock();
+ contention_count_++;
+ }
+ // ONLY access data after locking.
+ recursion_count_shadow_++;
+ if (1 == recursion_count_shadow_)
+ acquisition_count_++;
+ else if (2 == recursion_count_shadow_ && !recursion_used_)
+ // Usage Note: Set a break point to debug.
+ recursion_used_ = true;
+#endif // NDEBUG
+}
+
+void Lock::Release() {
+ --recursion_count_shadow_; // ONLY access while lock is still held.
+#ifndef NDEBUG
+ DCHECK(0 <= recursion_count_shadow_);
+#endif
+ lock_.Unlock();
+}
+
+bool Lock::Try() {
+ if (lock_.Try()) {
+ recursion_count_shadow_++;
+#ifndef NDEBUG
+ if (1 == recursion_count_shadow_)
+ acquisition_count_++;
+ else if (2 == recursion_count_shadow_ && !recursion_used_)
+ // Usage Note: Set a break point to debug.
+ recursion_used_ = true;
+#endif
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// GetCurrentThreadRecursionCount returns the number of nested Acquire() calls
+// that have been made by the current thread holding this lock. The calling
+// thread is ***REQUIRED*** to be *currently* holding the lock. If that
+// calling requirement is violated, the return value is not well defined.
+// Return results are guaranteed correct if the caller has acquired this lock.
+// The return results might be incorrect otherwise.
+// This method is designed to be fast in non-debug mode by co-opting
+// synchronization using lock_ (no additional synchronization is used), but in
+// debug mode it slowly and carefully validates the requirement (and fires a
+// a DCHECK if it was called incorrectly).
+int32 Lock::GetCurrentThreadRecursionCount() {
+#ifndef NDEBUG
+ // If this DCHECK fails, then the most probable cause is:
+ // This method was called by class AutoUnlock during processing of a
+ // Wait() call made into the ConditonVariable class. That call to
+ // Wait() was made (incorrectly) without first Aquiring this Lock
+ // instance.
+ lock_.Lock();
+ int temp = recursion_count_shadow_;
+ lock_.Unlock();
+ // Unit tests catch an exception, so we need to be careful to test
+ // outside the critical section, since the Leave would be skipped!?!
+ DCHECK(temp >= 1); // Allow unit test exception only at end of method.
+#endif // DEBUG
+
+ // We hold lock, so this *is* correct value.
+ return recursion_count_shadow_;
+}
+
+
+AutoUnlock::AutoUnlock(Lock& lock) : lock_(&lock), release_count_(0) {
+ // We require our caller have the lock, so we can call for recursion count.
+ // CRITICALLY: Fetch value before we release the lock.
+ int32 count = lock_->GetCurrentThreadRecursionCount();
+ DCHECK(count > 0); // Make sure we owned the lock.
+ while (count-- > 0) {
+ release_count_++;
+ lock_->Release();
+ }
+}
+
+AutoUnlock::~AutoUnlock() {
+ DCHECK(release_count_ >= 0);
+ while (release_count_-- > 0)
+ lock_->Acquire();
+}
diff --git a/base/lock.h b/base/lock.h
new file mode 100644
index 0000000..9613da0
--- /dev/null
+++ b/base/lock.h
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOCK_H__
+#define BASE_LOCK_H__
+
+#include "base/lock_impl.h"
+
+// A convenient wrapper for a critical section.
+//
+// NOTE: A thread may acquire the same lock multiple times, but it must call
+// Release for each call to Acquire in order to finally release the lock.
+//
+// Complication: UnitTest for DeathTests catch DCHECK exceptions, so we need
+// to write code assuming DCHECK will throw. This means we need to save any
+// assertable value in a local until we can safely throw.
+class Lock {
+ public:
+ Lock();
+ ~Lock();
+ void Acquire();
+ void Release();
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by something else, immediately return false.
+ bool Try();
+
+ private:
+ LockImpl lock_; // User-supplied underlying lock implementation.
+
+ // All private data is implicitly protected by spin_lock_.
+ // Be VERY careful to only access under that lock.
+ int32 recursion_count_shadow_;
+
+ // Allow access to GetCurrentThreadRecursionCount()
+ friend class AutoUnlock;
+ int32 GetCurrentThreadRecursionCount();
+
+#ifndef NDEBUG
+ // Even in Debug mode, the expensive tallies won't be calculated by default.
+ bool recursion_used_;
+ int32 acquisition_count_;
+
+ int32 contention_count_;
+#endif // NDEBUG
+
+ DISALLOW_EVIL_CONSTRUCTORS(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+class AutoLock {
+ public:
+ AutoLock(Lock& lock) : lock_(lock) {
+ lock_.Acquire();
+ }
+
+ ~AutoLock() {
+ lock_.Release();
+ }
+
+ private:
+ Lock& lock_;
+ DISALLOW_EVIL_CONSTRUCTORS(AutoLock);
+};
+
+// A helper macro to perform a single operation (expressed by expr)
+// in a lock
+#define LOCKED_EXPRESSION(lock, expr) \
+ do { \
+ AutoLock _auto_lock(lock); \
+ (expr); \
+ } while (0)
+
+// AutoUnlock is a helper class for ConditionVariable instances
+// that is analogous to AutoLock. It provides for nested Releases
+// of a lock for the Wait functionality of a ConditionVariable class.
+// The destructor automatically does the corresponding Acquire
+// calls (to return to the initial nested lock state).
+
+// Instances of AutoUnlock can ***ONLY*** validly be constructed if the
+// caller currently holds the lock provided as the constructor's argument.
+// If that ***REQUIREMENT*** is violated in debug mode, a DCHECK will
+// be generated in the Lock class. In production (non-debug),
+// the results are undefined (and probably bad) if the caller
+// is not already holding the indicated lock.
+class ConditionVariable;
+class AutoUnlock {
+ private: // Everything is private, so only our friend can use us.
+ friend class ConditionVariable; // The only user of this class.
+ explicit AutoUnlock(Lock& lock);
+ ~AutoUnlock();
+
+ Lock* lock_;
+ int release_count_;
+};
+
+#endif // BASE_LOCK_H__
diff --git a/base/lock_impl.h b/base/lock_impl.h
new file mode 100644
index 0000000..331bdc6
--- /dev/null
+++ b/base/lock_impl.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOCK_IMPL_H__
+#define BASE_LOCK_IMPL_H__
+
+#include "base/basictypes.h"
+
+#if defined(WIN32)
+#include <Windows.h>
+#elif defined(__APPLE__)
+#include <libkern/OSAtomic.h>
+#endif
+
+// This class implements the underlying platform-specific spin-lock mechanism
+// used for the Lock class. Most users should not use LockImpl directly, but
+// should instead use Lock.
+class LockImpl {
+ public:
+ LockImpl();
+ ~LockImpl();
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by something else, immediately return false.
+ bool Try();
+
+ // Take the lock, blocking until it is available if necessary.
+ void Lock();
+
+ // Release the lock. This must only be called by the lock's holder: after
+ // a successful call to Try, or a call to Lock.
+ void Unlock();
+
+ private:
+#if defined(WIN32)
+ CRITICAL_SECTION critical_section_;
+#elif defined(__APPLE__)
+ OSSpinLock spin_lock_;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(LockImpl);
+};
+
+class AutoLockImpl {
+ public:
+ AutoLockImpl(LockImpl* lock_impl)
+ : lock_impl_(lock_impl) {
+ lock_impl_->Lock();
+ }
+
+ ~AutoLockImpl() {
+ lock_impl_->Unlock();
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(AutoLockImpl);
+
+ LockImpl* lock_impl_;
+};
+
+#endif // BASE_LOCK_IMPL_H__
diff --git a/base/lock_impl_mac.cc b/base/lock_impl_mac.cc
new file mode 100644
index 0000000..fcb630d
--- /dev/null
+++ b/base/lock_impl_mac.cc
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/lock_impl.h"
+
+LockImpl::LockImpl()
+ : spin_lock_(0) {
+}
+
+LockImpl::~LockImpl() {
+}
+
+bool LockImpl::Try() {
+ return ::OSSpinLockTry(&spin_lock_);
+}
+
+void LockImpl::Lock() {
+ ::OSSpinLockLock(&spin_lock_);
+}
+
+void LockImpl::Unlock() {
+ ::OSSpinLockUnlock(&spin_lock_);
+}
diff --git a/base/lock_impl_win.cc b/base/lock_impl_win.cc
new file mode 100644
index 0000000..a45cbdd
--- /dev/null
+++ b/base/lock_impl_win.cc
@@ -0,0 +1,50 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/lock_impl.h"
+
+LockImpl::LockImpl() {
+ ::InitializeCriticalSection(&critical_section_);
+}
+
+LockImpl::~LockImpl() {
+ ::DeleteCriticalSection(&critical_section_);
+}
+
+bool LockImpl::Try() {
+ return ::TryEnterCriticalSection(&critical_section_) != FALSE;
+}
+
+void LockImpl::Lock() {
+ ::EnterCriticalSection(&critical_section_);
+}
+
+void LockImpl::Unlock() {
+ ::LeaveCriticalSection(&critical_section_);
+}
diff --git a/base/logging.cc b/base/logging.cc
new file mode 100644
index 0000000..eba177e
--- /dev/null
+++ b/base/logging.cc
@@ -0,0 +1,396 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <ctime>
+#include <iomanip>
+#include <cstring>
+#include <windows.h>
+#include <algorithm>
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/lock_impl.h"
+#include "base/logging.h"
+
+namespace logging {
+
+bool g_enable_dcheck = false;
+
+const char* const log_severity_names[LOG_NUM_SEVERITIES] = {
+ "INFO", "WARNING", "ERROR", "FATAL" };
+
+int min_log_level = 0;
+LogLockingState lock_log_file = LOCK_LOG_FILE;
+LoggingDestination logging_destination = LOG_ONLY_TO_FILE;
+
+const int kMaxFilteredLogLevel = LOG_WARNING;
+char* log_filter_prefix = NULL;
+
+// which log file to use? This is initialized by InitLogging or
+// will be lazily initialized to the default value when it is
+// first needed.
+wchar_t log_file_name[MAX_PATH] = { 0 };
+
+// this file is lazily opened and the handle may be NULL
+HANDLE log_file = NULL;
+
+// what should be prepended to each message?
+bool log_process_id = false;
+bool log_thread_id = false;
+bool log_timestamp = true;
+bool log_tickcount = false;
+
+// An assert handler override specified by the client to be called instead of
+// the debug message dialog.
+LogAssertHandlerFunction log_assert_handler = NULL;
+
+// The lock is used if log file locking is false. It helps us avoid problems
+// with multiple threads writing to the log file at the same time. Use
+// LockImpl directly instead of using Lock, because Lock makes logging calls.
+static LockImpl* log_lock = NULL;
+
+// When we don't use a lock, we are using a global mutex. We need to do this
+// because LockFileEx is not thread safe.
+HANDLE log_mutex = NULL;
+
+// Called by logging functions to ensure that debug_file is initialized
+// and can be used for writing. Returns false if the file could not be
+// initialized. debug_file will be NULL in this case.
+bool InitializeLogFileHandle() {
+ if (log_file)
+ return true;
+
+ if (!log_file_name[0]) {
+ // nobody has called InitLogging to specify a debug log file, so here we
+ // initialize the log file name to the default
+ GetModuleFileName(NULL, log_file_name, MAX_PATH);
+ wchar_t* last_backslash = wcsrchr(log_file_name, '\\');
+ if (last_backslash)
+ last_backslash[1] = 0; // name now ends with the backslash
+ wcscat_s(log_file_name, L"debug.log");
+ }
+
+ log_file = CreateFile(log_file_name, GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+ // try the current directory
+ log_file = CreateFile(L".\\debug.log", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) {
+ log_file = NULL;
+ return false;
+ }
+ }
+ SetFilePointer(log_file, 0, 0, FILE_END);
+ return true;
+}
+
+void InitLogMutex() {
+ if (!log_mutex) {
+ // \ is not a legal character in mutex names so we replace \ with /
+ std::wstring safe_name(log_file_name);
+ std::replace(safe_name.begin(), safe_name.end(), '\\', '/');
+ std::wstring t(L"Global\\");
+ t.append(safe_name);
+ log_mutex = ::CreateMutex(NULL, FALSE, t.c_str());
+ }
+}
+
+void InitLogging(const wchar_t* new_log_file, LoggingDestination logging_dest,
+ LogLockingState lock_log, OldFileDeletionState delete_old) {
+ g_enable_dcheck = CommandLine().HasSwitch(switches::kEnableDCHECK);
+
+ if (log_file) {
+ // calling InitLogging twice or after some log call has already opened the
+ // default log file will re-initialize to the new options
+ CloseHandle(log_file);
+ log_file = NULL;
+ }
+
+ lock_log_file = lock_log;
+ logging_destination = logging_dest;
+
+ // ignore file options if logging is disabled or only to system
+ if (logging_destination == LOG_NONE ||
+ logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG)
+ return;
+
+ wcscpy_s(log_file_name, MAX_PATH, new_log_file);
+ if (delete_old == DELETE_OLD_LOG_FILE)
+ DeleteFile(log_file_name);
+
+ if (lock_log_file == LOCK_LOG_FILE) {
+ InitLogMutex();
+ } else if (!log_lock) {
+ log_lock = new LockImpl();
+ }
+
+ InitializeLogFileHandle();
+}
+
+void SetMinLogLevel(int level) {
+ min_log_level = level;
+}
+
+int GetMinLogLevel() {
+ return min_log_level;
+}
+
+void SetLogFilterPrefix(const char* filter) {
+ if (log_filter_prefix) {
+ delete[] log_filter_prefix;
+ log_filter_prefix = NULL;
+ }
+
+ if (filter) {
+ size_t size = strlen(filter)+1;
+ log_filter_prefix = new char[size];
+ strcpy_s(log_filter_prefix, size, filter);
+ }
+}
+
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+ bool enable_timestamp, bool enable_tickcount) {
+ log_process_id = enable_process_id;
+ log_thread_id = enable_thread_id;
+ log_timestamp = enable_timestamp;
+ log_tickcount = enable_tickcount;
+}
+
+void SetLogAssertHandler(LogAssertHandlerFunction handler) {
+ log_assert_handler = handler;
+}
+
+// Displays a message box to the user with the error message in it. For
+// Windows programs, it's possible that the message loop is messed up on
+// a fatal error, and creating a MessageBox will cause that message loop
+// to be run. Instead, we try to spawn another process that displays its
+// command line. We look for "Debug Message.exe" in the same directory as
+// the application. If it exists, we use it, otherwise, we use a regular
+// message box.
+void DisplayDebugMessage(const std::string& str) {
+ if (str.empty())
+ return;
+
+ // look for the debug dialog program next to our application
+ wchar_t prog_name[MAX_PATH];
+ GetModuleFileNameW(NULL, prog_name, MAX_PATH);
+ wchar_t* backslash = wcsrchr(prog_name, '\\');
+ if (backslash)
+ backslash[1] = 0;
+ wcscat_s(prog_name, MAX_PATH, L"debug_message.exe");
+
+ // stupid CreateProcess requires a non-const command line and may modify it.
+ // We also want to use the wide string
+ int charcount = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
+ if (!charcount)
+ return;
+ scoped_array<wchar_t> cmdline(new wchar_t[charcount]);
+ if (!MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, cmdline.get(), charcount))
+ return;
+
+ STARTUPINFO startup_info;
+ memset(&startup_info, 0, sizeof(startup_info));
+ startup_info.cb = sizeof(startup_info);
+
+ PROCESS_INFORMATION process_info;
+ if (CreateProcessW(prog_name, cmdline.get(), NULL, NULL, false, 0, NULL,
+ NULL, &startup_info, &process_info)) {
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+ CloseHandle(process_info.hThread);
+ CloseHandle(process_info.hProcess);
+ } else {
+ // debug process broken, let's just do a message box
+ MessageBoxW(NULL, cmdline.get(), L"Fatal error",
+ MB_OK | MB_ICONHAND | MB_TOPMOST);
+ }
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ int ctr)
+ : severity_(severity) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, const CheckOpString& result)
+ : severity_(LOG_FATAL) {
+ Init(file, line);
+ stream_ << "Check failed: " << (*result.str_);
+}
+
+LogMessage::LogMessage(const char* file, int line)
+ : severity_(LOG_INFO) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+ : severity_(severity) {
+ Init(file, line);
+}
+
+// writes the common header info to the stream
+void LogMessage::Init(const char* file, int line) {
+ // log only the filename
+ const char* last_slash = strrchr(file, '\\');
+ if (last_slash)
+ file = last_slash + 1;
+
+ // TODO(darin): It might be nice if the columns were fixed width.
+
+ stream_ << '[';
+ if (log_process_id)
+ stream_ << GetCurrentProcessId() << ':';
+ if (log_thread_id)
+ stream_ << GetCurrentThreadId() << ':';
+ if (log_timestamp) {
+ time_t t = time(NULL);
+#if _MSC_VER >= 1400
+ struct tm local_time = {0};
+ localtime_s(&local_time, &t);
+ struct tm* tm_time = &local_time;
+#else
+ struct tm* tm_time = localtime(&t);
+#endif
+ stream_ << std::setfill('0')
+ << std::setw(2) << 1 + tm_time->tm_mon
+ << std::setw(2) << tm_time->tm_mday
+ << '/'
+ << std::setw(2) << tm_time->tm_hour
+ << std::setw(2) << tm_time->tm_min
+ << std::setw(2) << tm_time->tm_sec
+ << ':';
+ }
+ if (log_tickcount)
+ stream_ << GetTickCount() << ':';
+ stream_ << log_severity_names[severity_] << ":" << file << "(" << line << ")] ";
+
+ message_start_ = stream_.tellp();
+}
+
+LogMessage::~LogMessage() {
+ // TODO(brettw) modify the macros so that nothing is executed when the log
+ // level is too high.
+ if (severity_ < min_log_level)
+ return;
+
+ std::string str_newline(stream_.str());
+ str_newline.append("\r\n");
+
+ if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel &&
+ str_newline.compare(message_start_, strlen(log_filter_prefix),
+ log_filter_prefix) != 0) {
+ return;
+ }
+
+ if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG ||
+ logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG)
+ OutputDebugStringA(str_newline.c_str());
+
+ // write to log file
+ if (logging_destination != LOG_NONE &&
+ logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG &&
+ InitializeLogFileHandle()) {
+ // we can have multiple threads and/or processes, so try to prevent them from
+ // clobbering each other's writes
+ if (lock_log_file == LOCK_LOG_FILE) {
+ // Ensure that the mutex is initialized in case the client app did not
+ // call InitLogging. This is not thread safe. See below
+ InitLogMutex();
+
+ DWORD r = ::WaitForSingleObject(log_mutex, INFINITE);
+ DCHECK(r != WAIT_ABANDONED);
+ } else {
+ // use the lock
+ if (!log_lock) {
+ // The client app did not call InitLogging, and so the lock has not
+ // been created. We do this on demand, but if two threads try to do
+ // this at the same time, there will be a race condition to create
+ // the lock. This is why InitLogging should be called from the main
+ // thread at the beginning of execution.
+ log_lock = new LockImpl();
+ }
+ log_lock->Lock();
+ }
+
+ SetFilePointer(log_file, 0, 0, SEEK_END);
+ DWORD num_written;
+ WriteFile(log_file, (void*)str_newline.c_str(), (DWORD)str_newline.length(), &num_written, NULL);
+
+ if (lock_log_file == LOCK_LOG_FILE) {
+ ReleaseMutex(log_mutex);
+ } else {
+ log_lock->Unlock();
+ }
+ }
+
+ if (severity_ == LOG_FATAL) {
+ // display a message or break into the debugger on a fatal error
+ if (::IsDebuggerPresent()) {
+ __debugbreak();
+ } else {
+ if (log_assert_handler) {
+ // make a copy of the string for the handler out of paranoia
+ log_assert_handler(std::string(stream_.str()));
+ } else {
+ // don't use the string with the newline, get a fresh version to send to
+ // the debug message process
+ DisplayDebugMessage(stream_.str());
+ // Crash the process to generate a dump.
+ __debugbreak();
+ }
+ }
+ }
+}
+
+void CloseLogFile() {
+ if (!log_file)
+ return;
+
+ CloseHandle(log_file);
+ log_file = NULL;
+}
+
+} // namespace logging
+
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) {
+ if (!wstr || !wstr[0])
+ return out;
+
+ // compute the length of the buffer we'll need
+ int charcount = WideCharToMultiByte(CP_UTF8, 0, wstr, -1,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0)
+ return out;
+
+ // convert
+ scoped_array<char> buf(new char[charcount]);
+ WideCharToMultiByte(CP_UTF8, 0, wstr, -1, buf.get(), charcount, NULL, NULL);
+ return out << buf.get();
+}
diff --git a/base/logging.h b/base/logging.h
new file mode 100644
index 0000000..615b1e0
--- /dev/null
+++ b/base/logging.h
@@ -0,0 +1,523 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_LOGGING_H__
+#define BASE_LOGGING_H__
+
+#include <string>
+#include <cstring>
+#include <sstream>
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+
+//
+// Optional message capabilities
+// -----------------------------
+// Assertion failed messages and fatal errors are displayed in a dialog box
+// before the application exits. However, running this UI creates a message
+// loop, which causes application messages to be processed and potentially
+// dispatched to existing application windows. Since the application is in a
+// bad state when this assertion dialog is displayed, these messages may not
+// get processed and hang the dialog, or the application might go crazy.
+//
+// Therefore, it can be beneficial to display the error dialog in a separate
+// process from the main application. When the logging system needs to display
+// a fatal error dialog box, it will look for a program called
+// "DebugMessage.exe" in the same directory as the application executable. It
+// will run this application with the message as the command line, and will
+// not include the name of the application as is traditional for easier
+// parsing.
+//
+// The code for DebugMessage.exe is only one line. In WinMain, do:
+// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0);
+//
+// If DebugMessage.exe is not found, the logging code will use a normal
+// MessageBox, potentially causing the problems discussed above.
+
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging. The way to log things is to stream
+// things to LOG(<a particular severity level>). E.g.,
+//
+// LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The above will cause log messages to be output on the 1st, 11th, 21st, ...
+// times it is executed. Note that the special COUNTER value is used to
+// identify which repetition is happening.
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+// DLOG(INFO) << "Found cookies";
+//
+// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles. LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+// LOG_ASSERT(assertion);
+// DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// There is also the special severity of DFATAL, which logs FATAL in
+// debug mode, ERROR in normal mode.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+
+namespace logging {
+
+// Where to record logging output? A flat file and/or system debug log via
+// OutputDebugString. Defaults to LOG_ONLY_TO_FILE.
+enum LoggingDestination { LOG_NONE,
+ LOG_ONLY_TO_FILE,
+ LOG_ONLY_TO_SYSTEM_DEBUG_LOG,
+ LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG };
+
+// Indicates that the log file should be locked when being written to.
+// Often, there is no locking, which is fine for a single threaded program.
+// If logging is being done from multiple threads or there can be more than
+// one process doing the logging, the file should be locked during writes to
+// make each log outut atomic. Other writers will block.
+//
+// All processes writing to the log file must have their locking set for it to
+// work properly. Defaults to DONT_LOCK_LOG_FILE.
+enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE };
+
+// On startup, should we delete or append to an existing log file (if any)?
+// Defaults to APPEND_TO_OLD_LOG_FILE.
+enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE };
+
+// Sets the log file name and other global logging state. Calling this function
+// is recommended, and is normally done at the beginning of application init.
+// If you don't call it, all the flags will be initialized to their default
+// values, and there is a race condition that may leak a critical section
+// object if two threads try to do the first log at the same time.
+// See the definition of the enums above for descriptions and default values.
+//
+// The default log file is initialized to "debug.log" in the application
+// directory. You probably don't want this, especially since the program
+// directory may not be writable on an enduser's system.
+#if defined(WIN32)
+void InitLogging(const wchar_t* log_file, LoggingDestination logging_dest,
+ LogLockingState lock_log, OldFileDeletionState delete_old);
+#else
+// TODO(avi): do we want to do a unification of character types here?
+void InitLogging(const char* log_file, LoggingDestination logging_dest,
+ LogLockingState lock_log, OldFileDeletionState delete_old);
+#endif
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged)
+// if this function is not called.
+void SetMinLogLevel(int level);
+
+// Gets the curreng log level.
+int GetMinLogLevel();
+
+// Sets the log filter prefix. Any log message below LOG_ERROR severity that
+// doesn't start with this prefix with be silently ignored. The filter defaults
+// to NULL (everything is logged) if this function is not called. Messages
+// with severity of LOG_ERROR or higher will not be filtered.
+void SetLogFilterPrefix(const char* filter);
+
+// Sets the common items you want to be prepended to each log message.
+// process and thread IDs default to off, the timestamp defaults to on.
+// If this function is not called, logging defaults to writing the timestamp
+// only.
+void SetLogItems(bool enable_process_id, bool enable_thread_id,
+ bool enable_timestamp, bool enable_tickcount);
+
+// Sets the Log Assert Handler that will be used to notify of check failures.
+// The default handler shows a dialog box, however clients can use this
+// function to override with their own handling (e.g. a silent one for Unit
+// Tests)
+typedef void (*LogAssertHandlerFunction)(const std::string& str);
+void SetLogAssertHandler(LogAssertHandlerFunction handler);
+
+typedef int LogSeverity;
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// LOG_DFATAL_LEVEL is LOG_FATAL in debug mode, ERROR in normal mode
+#ifdef NDEBUG
+const LogSeverity LOG_DFATAL_LEVEL = LOG_ERROR;
+#else
+const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL;
+#endif
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_INFO \
+ logging::LogMessage(__FILE__, __LINE__)
+#define COMPACT_GOOGLE_LOG_WARNING \
+ logging::LogMessage(__FILE__, __LINE__, logging::LOG_WARNING)
+#define COMPACT_GOOGLE_LOG_ERROR \
+ logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+#define COMPACT_GOOGLE_LOG_FATAL \
+ logging::LogMessage(__FILE__, __LINE__, logging::LOG_FATAL)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+ logging::LogMessage(__FILE__, __LINE__, logging::LOG_DFATAL_LEVEL)
+
+// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets
+// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us
+// to keep using this syntax, we define this macro to do the same thing
+// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that
+// the Windows SDK does for consistency.
+#define ERROR 0
+#define COMPACT_GOOGLE_LOG_0 \
+ logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+
+#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+#define SYSLOG(severity) LOG(severity)
+
+#define LOG_IF(severity, condition) \
+ !(condition) ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition)
+
+#define LOG_ASSERT(condition) \
+ LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+ SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+#define CHECK(condition) \
+ LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// A container for a string pointer which can be evaluated to a bool -
+// true iff the pointer is NULL.
+struct CheckOpString {
+ CheckOpString(std::string* str) : str_(str) { }
+ // No destructor: if str_ is non-NULL, we're about to LOG(FATAL),
+ // so there's no point in cleaning up str_.
+ operator bool() const { return str_ != NULL; }
+ std::string* str_;
+};
+
+// Build the error message string. This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+ std::ostringstream ss;
+ ss << names << " (" << v1 << " vs. " << v2 << ")";
+ std::string* msg = new std::string(ss.str());
+ return msg;
+}
+
+extern std::string* MakeCheckOpStringIntInt(int v1, int v2, const char* names);
+
+template<int, int>
+std::string* MakeCheckOpString(const int& v1, const int& v2, const char* names) {
+ return MakeCheckOpStringIntInt(v1, v2, names);
+}
+
+// Plus some debug-logging macros that get compiled to nothing for production
+//
+// DEBUG_MODE is for uses like
+// if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+// #ifndef NDEBUG
+// foo.CheckThatFoo();
+// #endif
+
+#ifndef NDEBUG
+
+#define DLOG(severity) LOG(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+
+// debug-only checking. not executed in NDEBUG mode.
+enum { DEBUG_MODE = 1 };
+#define DCHECK(condition) \
+ LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2) \
+ if (logging::CheckOpString _result = \
+ logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+ logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Helper functions for string comparisons.
+// To avoid bloat, the definitions are in logging.cc.
+#define DECLARE_DCHECK_STROP_IMPL(func, expected) \
+ std::string* Check##func##expected##Impl(const char* s1, \
+ const char* s2, \
+ const char* names);
+DECLARE_DCHECK_STROP_IMPL(strcmp, true)
+DECLARE_DCHECK_STROP_IMPL(strcmp, false)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, true)
+DECLARE_DCHECK_STROP_IMPL(_stricmp, false)
+#undef DECLARE_DCHECK_STROP_IMPL
+
+// Helper macro for string comparisons.
+// Don't use this macro directly in your code, use CHECK_STREQ et al below.
+#define DCHECK_STROP(func, op, expected, s1, s2) \
+ while (CheckOpString _result = \
+ logging::Check##func##expected##Impl((s1), (s2), \
+ #s1 " " #op " " #s2)) \
+ LOG(FATAL) << *_result.str_
+
+// String (char*) equality/inequality checks.
+// CASE versions are case-insensitive.
+//
+// Note that "s1" and "s2" may be temporary strings which are destroyed
+// by the compiler at the end of the current "full expression"
+// (e.g. DCHECK_STREQ(Foo().c_str(), Bar().c_str())).
+
+#define DCHECK_STREQ(s1, s2) DCHECK_STROP(strcmp, ==, true, s1, s2)
+#define DCHECK_STRNE(s1, s2) DCHECK_STROP(strcmp, !=, false, s1, s2)
+#define DCHECK_STRCASEEQ(s1, s2) DCHECK_STROP(_stricmp, ==, true, s1, s2)
+#define DCHECK_STRCASENE(s1, s2) DCHECK_STROP(_stricmp, !=, false, s1, s2)
+
+#define DCHECK_INDEX(I,A) DCHECK(I < (sizeof(A)/sizeof(A[0])))
+#define DCHECK_BOUND(B,A) DCHECK(B <= (sizeof(A)/sizeof(A[0])))
+
+#else // NDEBUG
+
+#define DLOG(severity) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_IF(severity, condition) \
+ true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity)
+
+#define DLOG_ASSERT(condition) \
+ true ? (void) 0 : LOG_ASSERT(condition)
+
+enum { DEBUG_MODE = 0 };
+
+// This macro can be followed by a sequence of stream parameters in
+// non-debug mode. The DCHECK and friends macros use this so that
+// the expanded expression DCHECK(foo) << "asdf" is still syntactically
+// valid, even though the expression will get optimized away.
+#define NDEBUG_EAT_STREAM_PARAMETERS \
+ logging::LogMessage(__FILE__, __LINE__).stream()
+
+// Set to true in InitLogging when we want to enable the dchecks in release.
+extern bool g_enable_dcheck;
+#define DCHECK(condition) \
+ !logging::g_enable_dcheck ? void (0) : \
+ LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2) \
+ if (logging::g_enable_dcheck) \
+ if (logging::CheckOpString _result = \
+ logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \
+ logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+#define DCHECK_STREQ(str1, str2) \
+ while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASEEQ(str1, str2) \
+ while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRNE(str1, str2) \
+ while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#define DCHECK_STRCASENE(str1, str2) \
+ while (false) NDEBUG_EAT_STREAM_PARAMETERS
+
+#endif // NDEBUG
+
+// Helper functions for DCHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_DCHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ } \
+ inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ }
+DEFINE_DCHECK_OP_IMPL(EQ, ==)
+DEFINE_DCHECK_OP_IMPL(NE, !=)
+DEFINE_DCHECK_OP_IMPL(LE, <=)
+DEFINE_DCHECK_OP_IMPL(LT, < )
+DEFINE_DCHECK_OP_IMPL(GE, >=)
+DEFINE_DCHECK_OP_IMPL(GT, > )
+#undef DEFINE_DCHECK_OP_IMPL
+
+// Equality/Inequality checks - compare two values, and log a LOG_FATAL message
+// including the two values when the result is not as expected. The values
+// must have operator<<(ostream, ...) defined.
+//
+// You may append to the error message like so:
+// DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here. In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+// DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+
+#define NOTREACHED() DCHECK(false)
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message. You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though. You should use the LOG() macro (and variants thereof)
+// above.
+class LogMessage {
+ public:
+ LogMessage(const char* file, int line, LogSeverity severity, int ctr);
+
+ // Two special constructors that generate reduced amounts of code at
+ // LOG call sites for common cases.
+ //
+ // Used for LOG(INFO): Implied are:
+ // severity = LOG_INFO, ctr = 0
+ //
+ // Using this constructor instead of the more complex constructor above
+ // saves a couple of bytes per call site.
+ LogMessage(const char* file, int line);
+
+ // Used for LOG(severity) where severity != INFO. Implied
+ // are: ctr = 0
+ //
+ // Using this constructor instead of the more complex constructor above
+ // saves a couple of bytes per call site.
+ LogMessage(const char* file, int line, LogSeverity severity);
+
+ // A special constructor used for check failures.
+ // Implied severity = LOG_FATAL
+ LogMessage(const char* file, int line, const CheckOpString& result);
+
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ void Init(const char* file, int line);
+
+ LogSeverity severity_;
+ std::ostringstream stream_;
+ int message_start_; // offset of the start of the message (past prefix info).
+
+ DISALLOW_EVIL_CONSTRUCTORS(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+ LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() { }
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+// statements, there's no guarantee that it will stay closed
+// after this call.
+void CloseLogFile();
+
+} // namespace Logging
+
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these operators.
+std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+ return out << wstr.c_str();
+}
+
+#endif // BASE_LOGGING_H__
diff --git a/base/md5.cc b/base/md5.cc
new file mode 100644
index 0000000..f2e1c4a
--- /dev/null
+++ b/base/md5.cc
@@ -0,0 +1,279 @@
+// The original file was copied from sqlite, and was in the public domain.
+// Modifications Copyright 2006 Google Inc. All Rights Reserved
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include <string>
+
+#include "base/md5.h"
+
+#include "base/basictypes.h"
+
+struct Context {
+ uint32 buf[4];
+ uint32 bits[2];
+ unsigned char in[64];
+};
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse (unsigned char *buf, unsigned longs){
+ uint32 t;
+ do {
+ t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 |
+ ((unsigned)buf[1]<<8 | buf[0]);
+ *(uint32 *)buf = t;
+ buf += 4;
+ } while (--longs);
+}
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+ ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data. MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(uint32 buf[4], const uint32 in[16]){
+ register uint32 a, b, c, d;
+
+ a = buf[0];
+ b = buf[1];
+ c = buf[2];
+ d = buf[3];
+
+ MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
+ MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
+ MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
+ MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
+ MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
+ MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
+ MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
+ MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
+ MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
+ MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
+ MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
+ MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
+ MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
+ MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
+ MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
+ MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
+
+ MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
+ MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
+ MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
+ MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
+ MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
+ MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
+ MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
+ MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
+ MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
+ MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
+ MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
+ MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
+ MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
+ MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
+ MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
+ MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
+
+ MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
+ MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
+ MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
+ MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
+ MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
+ MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
+ MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
+ MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
+ MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
+ MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
+ MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
+ MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
+ MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
+ MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
+ MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
+ MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
+
+ MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
+ MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
+ MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
+ MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
+ MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
+ MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
+ MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
+ MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
+ MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
+ MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
+ MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
+ MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
+ MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
+ MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
+ MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(MD5Context *pCtx){
+ struct Context *ctx = (struct Context *)pCtx;
+ ctx->buf[0] = 0x67452301;
+ ctx->buf[1] = 0xefcdab89;
+ ctx->buf[2] = 0x98badcfe;
+ ctx->buf[3] = 0x10325476;
+ ctx->bits[0] = 0;
+ ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(MD5Context *pCtx, const void *inbuf, size_t len){
+ struct Context *ctx = (struct Context *)pCtx;
+ const unsigned char* buf = (const unsigned char*)inbuf;
+ uint32 t;
+
+ /* Update bitcount */
+
+ t = ctx->bits[0];
+ if ((ctx->bits[0] = t + ((uint32)len << 3)) < t)
+ ctx->bits[1]++; /* Carry from low to high */
+ ctx->bits[1] += static_cast<uint32>(len >> 29);
+
+ t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */
+
+ /* Handle any leading odd-sized chunks */
+
+ if ( t ) {
+ unsigned char *p = (unsigned char *)ctx->in + t;
+
+ t = 64-t;
+ if (len < t) {
+ memcpy(p, buf, len);
+ return;
+ }
+ memcpy(p, buf, t);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += t;
+ len -= t;
+ }
+
+ /* Process data in 64-byte chunks */
+
+ while (len >= 64) {
+ memcpy(ctx->in, buf, 64);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ buf += 64;
+ len -= 64;
+ }
+
+ /* Handle any remaining bytes of data. */
+
+ memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(MD5Digest* digest, MD5Context *pCtx){
+ struct Context *ctx = (struct Context *)pCtx;
+ unsigned count;
+ unsigned char *p;
+
+ /* Compute number of bytes mod 64 */
+ count = (ctx->bits[0] >> 3) & 0x3F;
+
+ /* Set the first char of padding to 0x80. This is safe since there is
+ always at least one byte free */
+ p = ctx->in + count;
+ *p++ = 0x80;
+
+ /* Bytes of padding needed to make 64 bytes */
+ count = 64 - 1 - count;
+
+ /* Pad out to 56 mod 64 */
+ if (count < 8) {
+ /* Two lots of padding: Pad the first block to 64 bytes */
+ memset(p, 0, count);
+ byteReverse(ctx->in, 16);
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+
+ /* Now fill the next block with 56 bytes */
+ memset(ctx->in, 0, 56);
+ } else {
+ /* Pad block to 56 bytes */
+ memset(p, 0, count-8);
+ }
+ byteReverse(ctx->in, 14);
+
+ /* Append length in bits and transform */
+ ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0];
+ ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1];
+
+ MD5Transform(ctx->buf, (uint32 *)ctx->in);
+ byteReverse((unsigned char *)ctx->buf, 4);
+ memcpy(digest->a, ctx->buf, 16);
+ memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */
+}
+
+std::string MD5DigestToBase16(const MD5Digest& digest){
+ static char const zEncode[] = "0123456789abcdef";
+
+ std::string ret;
+ ret.resize(32);
+
+ int j = 0;
+ for(int i = 0; i < 16; i ++){
+ int a = digest.a[i];
+ ret[j++] = zEncode[(a>>4)&0xf];
+ ret[j++] = zEncode[a & 0xf];
+ }
+ return ret;
+}
+
+void MD5Sum(const void* data, size_t length, MD5Digest* digest) {
+ MD5Context ctx;
+ MD5Init(&ctx);
+ MD5Update(&ctx, static_cast<const unsigned char*>(data), length);
+ MD5Final(digest, &ctx);
+}
+
+std::string MD5String(const std::string& str) {
+ MD5Digest digest;
+ MD5Sum(str.data(), str.length(), &digest);
+ return MD5DigestToBase16(digest);
+}
diff --git a/base/md5.h b/base/md5.h
new file mode 100644
index 0000000..5fc6795
--- /dev/null
+++ b/base/md5.h
@@ -0,0 +1,82 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MD5_H__
+#define BASE_MD5_H__
+
+#include <string>
+
+// These functions perform MD5 operations. The simplest call is MD5Sum to
+// generate the MD5 sum of the given data.
+//
+// You can also compute the MD5 sum of data incrementally by making multiple
+// calls to MD5Update:
+// MD5Context ctx; // intermediate MD5 data: do not use
+// MD5Init(&ctx);
+// MD5Update(&ctx, data1, length1);
+// MD5Update(&ctx, data2, length2);
+// ...
+//
+// MD5Digest digest; // the result of the computation
+// MD5Final(&digest, &ctx);
+//
+// You can call MD5DigestToBase16 to generate a string of the digest.
+
+// The output of an MD5 operation
+typedef struct MD5Digest_struct {
+ unsigned char a[16];
+} MD5Digest;
+
+// Used for storing intermediate data during an MD5 computation. Callers
+// should not access the data.
+typedef char MD5Context[88];
+
+// Computes the MD5 sum of the given data buffer with the given length.
+// The given 'digest' structure will be filled with the result data.
+void MD5Sum(const void* data, size_t length, MD5Digest* digest);
+
+// Initializes the given MD5 context structure for subsequent calls to
+// MD5Update.
+void MD5Init(MD5Context* context);
+
+// For the given buffer of data, updates the given MD5 context with the sum of
+// the data. You can call this any number of times during the computation,
+// exept that MD5Init must have been called first.
+void MD5Update(MD5Context* context, const void* buf, size_t len);
+
+// Finalizes the MD5 operation and fills the buffer with the digest.
+void MD5Final(MD5Digest* digest, MD5Context* pCtx);
+
+// Converts a digest into human-readable hexadecimal.
+std::string MD5DigestToBase16(const MD5Digest& digest);
+
+// Returns the MD5 (in hexadecimal) of a string.
+std::string MD5String(const std::string& str);
+
+#endif // BASE_MD5_H__
diff --git a/base/memory_debug.cc b/base/memory_debug.cc
new file mode 100644
index 0000000..6acfe83
--- /dev/null
+++ b/base/memory_debug.cc
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifdef PURIFY
+// this #define is used to prevent people from directly using pure.h
+// instead of memory_debug.h
+#define PURIFY_PRIVATE_INCLUDE
+#include "base/third_party/purify/pure.h"
+#endif
+
+#include "base/memory_debug.h"
+
+namespace base {
+
+bool MemoryDebug::memory_in_use_ = false;
+
+void MemoryDebug::SetMemoryInUseEnabled(bool enabled) {
+ memory_in_use_ = enabled;
+}
+
+void MemoryDebug::DumpAllMemoryInUse() {
+#ifdef PURIFY
+ if (memory_in_use_)
+ PurifyAllInuse();
+#endif
+}
+
+void MemoryDebug::DumpNewMemoryInUse() {
+#ifdef PURIFY
+ if (memory_in_use_)
+ PurifyNewInuse();
+#endif
+}
+
+void MemoryDebug::DumpAllLeaks() {
+#ifdef PURIFY
+ PurifyAllLeaks();
+#endif
+}
+
+void MemoryDebug::DumpNewLeaks() {
+#ifdef PURIFY
+ PurifyNewLeaks();
+#endif
+}
+
+void MemoryDebug::MarkAsInitialized(void* addr, size_t size) {
+#ifdef PURIFY
+ PurifyMarkAsInitialized(addr, size);
+#endif
+}
+
+} // namespace base
diff --git a/base/memory_debug.h b/base/memory_debug.h
new file mode 100644
index 0000000..c1283dc
--- /dev/null
+++ b/base/memory_debug.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Functions used to debug memory usage, leaks, and other memory issues.
+// All methods are effectively no-ops unless this program is being run through
+// a supported memory tool (currently, only Purify)
+
+#ifndef BASE_MEMORY_DEBUG_H_
+
+namespace base {
+
+class MemoryDebug {
+public:
+ // Since MIU messages are a lot of data, and we don't always want this data,
+ // we have a global switch. If disabled, *MemoryInUse are no-ops.
+ static void SetMemoryInUseEnabled(bool enabled);
+
+ // Dump information about all memory in use.
+ static void DumpAllMemoryInUse();
+ // Dump information about new memory in use since the last
+ // call to DumpAllMemoryInUse() or DumpNewMemoryInUse().
+ static void DumpNewMemoryInUse();
+
+ // Dump information about all current memory leaks.
+ static void DumpAllLeaks();
+ // Dump information about new memory leaks since the last
+ // call to DumpAllLeaks() or DumpNewLeaks()
+ static void DumpNewLeaks();
+
+ // Mark |size| bytes of memory as initialized, so it doesn't produce any UMRs
+ // or UMCs.
+ static void MarkAsInitialized(void* addr, size_t size);
+
+private:
+ static bool memory_in_use_;
+};
+
+} // namespace base
+
+#endif \ No newline at end of file
diff --git a/base/message_loop.cc b/base/message_loop.cc
new file mode 100644
index 0000000..623ec99
--- /dev/null
+++ b/base/message_loop.cc
@@ -0,0 +1,969 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <algorithm>
+
+#include "base/message_loop.h"
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "base/thread_local_storage.h"
+#include "base/win_util.h"
+
+// a TLS index to the message loop for the current thread
+// Note that if we start doing complex stuff in other static initializers
+// this could cause problems.
+/*static*/ TLSSlot MessageLoop::tls_index_ = ThreadLocalStorage::Alloc();
+
+//------------------------------------------------------------------------------
+
+static const wchar_t kWndClass[] = L"Chrome_MessageLoopWindow";
+
+// Windows Message numbers handled by WindowMessageProc.
+
+// Message sent to get an additional time slice for pumping (processing) another
+// task (a series of such messages creates a continuous task pump).
+static const int kMsgPumpATask = WM_USER + 1;
+
+// Message sent by Quit() to cause our main message pump to terminate as soon as
+// all pending task and message queues have been emptied.
+static const int kMsgQuit = WM_USER + 2;
+
+// Logical events for Histogram profiling. Run with -message-loop-histogrammer
+// to get an accounting of messages and actions taken on each thread.
+static const int kTaskRunEvent = WM_USER + 16; // 0x411
+static const int kSleepingApcEvent = WM_USER + 17; // 0x411
+static const int kPollingSignalEvent = WM_USER + 18; // 0x412
+static const int kSleepingSignalEvent = WM_USER + 19; // 0x413
+static const int kTimerEvent = WM_USER + 20; // 0x414
+
+// Provide range of message IDs for use in histogramming and debug display.
+static const int kLeastNonZeroMessageId = 1;
+static const int kMaxMessageId = 1099;
+static const int kNumberOfDistinctMessagesDisplayed = 1100;
+
+//------------------------------------------------------------------------------
+
+static LRESULT CALLBACK MessageLoopWndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ switch (message) {
+ case kMsgQuit:
+ case kMsgPumpATask: {
+ UINT_PTR message_loop_id = static_cast<UINT_PTR>(wparam);
+ MessageLoop* current_message_loop =
+ reinterpret_cast<MessageLoop*>(message_loop_id);
+ DCHECK(MessageLoop::current() == current_message_loop);
+ return current_message_loop->MessageWndProc(hwnd, message, wparam,
+ lparam);
+ }
+ }
+ return ::DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+#ifndef NDEBUG
+// Force exercise of polling model.
+#define CHROME_MAXIMUM_WAIT_OBJECTS 8
+#else
+#define CHROME_MAXIMUM_WAIT_OBJECTS MAXIMUM_WAIT_OBJECTS
+#endif
+
+//------------------------------------------------------------------------------
+// A strategy of -1 uses the default case. All strategies are selected as
+// positive integers.
+// static
+int MessageLoop::strategy_selector_ = -1;
+
+// static
+void MessageLoop::SetStrategy(int strategy) {
+ DCHECK(-1 == strategy_selector_);
+ strategy_selector_ = strategy;
+}
+
+//------------------------------------------------------------------------------
+// Upon a SEH exception in this thread, it restores the original unhandled
+// exception filter.
+static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) {
+ ::SetUnhandledExceptionFilter(old_filter);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
+// Retrieves a pointer to the current unhandled exception filter. There
+// is no standalone getter method.
+static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() {
+ LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL;
+ top_filter = ::SetUnhandledExceptionFilter(0);
+ ::SetUnhandledExceptionFilter(top_filter);
+ return top_filter;
+}
+
+//------------------------------------------------------------------------------
+
+MessageLoop::MessageLoop() : message_hwnd_(NULL),
+ exception_restoration_(false),
+ nestable_tasks_allowed_(true),
+ dispatcher_(NULL),
+ quit_received_(false),
+ quit_now_(false),
+ task_pump_message_pending_(false),
+ run_depth_(0) {
+ DCHECK(tls_index_) << "static initializer failed";
+ DCHECK(!current()) << "should only have one message loop per thread";
+ ThreadLocalStorage::Set(tls_index_, this);
+ InitMessageWnd();
+}
+
+MessageLoop::~MessageLoop() {
+ DCHECK(this == current());
+ ThreadLocalStorage::Set(tls_index_, NULL);
+ DCHECK(!dispatcher_);
+ DCHECK(!quit_received_ && !quit_now_);
+ // Most tasks that have not been Run() are deleted in the |timer_manager_|
+ // destructor after we remove our tls index. We delete the tasks in our
+ // queues here so their destuction is similar to the tasks in the
+ // |timer_manager_|.
+ DeletePendingTasks();
+ ReloadWorkQueue();
+ DeletePendingTasks();
+}
+
+void MessageLoop::SetThreadName(const std::string& thread_name) {
+ DCHECK(thread_name_.empty());
+ thread_name_ = thread_name;
+ StartHistogrammer();
+}
+
+void MessageLoop::AddObserver(Observer *obs) {
+ DCHECK(this == current());
+ observers_.AddObserver(obs);
+}
+
+void MessageLoop::RemoveObserver(Observer *obs) {
+ DCHECK(this == current());
+ observers_.RemoveObserver(obs);
+}
+
+void MessageLoop::Run() {
+ Run(NULL);
+}
+
+// Runs the loop in two different SEH modes:
+// enable_SEH_restoration_ = false : any unhandled exception goes to the last
+// one that calls SetUnhandledExceptionFilter().
+// enable_SEH_restoration_ = true : any unhandled exception goes to the filter
+// that was existed before the loop was run.
+void MessageLoop::Run(Dispatcher* dispatcher) {
+ if (exception_restoration_) {
+ LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
+ __try {
+ RunInternal(dispatcher);
+ } __except(SEHFilter(current_filter)) {
+ }
+ } else {
+ RunInternal(dispatcher);
+ }
+}
+
+//------------------------------------------------------------------------------
+// Methods supporting various strategies for servicing the numerous queues.
+// IF this was just a simple PeekMessage() loop (servicing all passible work
+// queues), then Windows would try to achieve the following order according to
+// MSDN documentation about PeekMessage with no filter):
+// * Sent messages
+// * Posted messages
+// * Sent messages (again)
+// * WM_PAINT messages
+// * WM_TIMER messages
+//
+// Summary: none of the above classes is starved, and sent messages has twice
+// the chance of being processed (i.e., reduced service time).
+
+void MessageLoop::RunInternal(Dispatcher* dispatcher) {
+ // Preserve ability to be called recursively.
+ ScopedStateSave save(this); // State is restored on exit.
+ dispatcher_ = dispatcher;
+ StartHistogrammer();
+
+ DCHECK(this == current());
+ //
+ // Process all pending messages and signaled objects.
+ //
+ // Flush these queues before exiting due to a kMsgQuit or else we risk not
+ // shutting down properly as some operations may depend on further event
+ // processing. (Note: some tests may use quit_now_ to exit more swiftly,
+ // and leave messages pending, so don't assert the above fact).
+ //
+
+ RunTraditional();
+ DCHECK(quit_received_ || quit_now_);
+}
+
+typedef bool (MessageLoop::*ProcessingMethod)();
+typedef ProcessingMethod ProcessingMethods[];
+
+void MessageLoop::RunTraditional() {
+ run_depth_++;
+ for (;;) {
+ // If we do any work, we may create more messages etc., and more work
+ // may possibly be waiting in another task group. In addition, each method
+ // call here typically limits work to 1 (worst case 2) items. As a result,
+ // when we (for example) ProcessNextWindowsMessage() there is a good chance
+ // there are still more waiting (same thing for ProcessNextDeferredTask(),
+ // which responds to only one signaled object.). On the other hand, when
+ // any of these methods return having done no work, then it is pretty
+ // unlikely that calling them again quickly will find any work to do.
+ // Finally, if they all say they had no work, then it is a good time to
+ // consider sleeping (waiting) for more work.
+ bool more_work_is_plausible = false;
+ more_work_is_plausible |= ProcessNextWindowsMessage();
+ if (quit_now_)
+ break;
+
+ more_work_is_plausible |= ProcessNextDeferredTask();
+ more_work_is_plausible |= ProcessNextObject();
+ if (more_work_is_plausible)
+ continue;
+
+ if (quit_received_)
+ break;
+
+ // Run any timer that is ready to run. It may create messages etc.
+ if (ProcessSomeTimers())
+ continue;
+
+ // We run delayed non nestable tasks only after all nestable tasks have
+ // run, to preserve FIFO ordering.
+ more_work_is_plausible = ProcessNextDelayedNonNestableTask();
+ if (more_work_is_plausible)
+ continue;
+
+ // We service APCs in WaitForWork, without returning.
+ WaitForWork(); // Wait (sleep) until we have work to do again.
+ }
+
+ run_depth_--;
+}
+
+bool MessageLoop::ProcessNextDelayedNonNestableTask() {
+ if (run_depth_ != 1)
+ return false;
+
+ if (delayed_non_nestable_queue_.Empty())
+ return false;
+
+ RunTask(delayed_non_nestable_queue_.Pop());
+ return true;
+}
+
+//------------------------------------------------------------------------------
+// Wrapper functions for use in above message loop frameworks.
+
+bool MessageLoop::ProcessNextDeferredTask() {
+ ReloadWorkQueue();
+ return QueueOrRunTask(NULL);
+}
+
+bool MessageLoop::ProcessSomeTimers() {
+ return timer_manager_.RunSomePendingTimers();
+}
+
+//------------------------------------------------------------------------------
+
+void MessageLoop::Quit() {
+ EnsureMessageGetsPosted(kMsgQuit);
+}
+
+bool MessageLoop::WatchObject(HANDLE object, Watcher* watcher) {
+ DCHECK(this == current());
+ DCHECK(object);
+ DCHECK_NE(object, INVALID_HANDLE_VALUE);
+
+ std::vector<HANDLE>::iterator it = find(objects_.begin(), objects_.end(),
+ object);
+ if (watcher) {
+ if (it == objects_.end()) {
+ static size_t warning_multiple = 1;
+ if (objects_.size() >= warning_multiple * MAXIMUM_WAIT_OBJECTS / 2) {
+ LOG(INFO) << "More than " << warning_multiple * MAXIMUM_WAIT_OBJECTS / 2
+ << " objects being watched";
+ // This DCHECK() is an artificial limitation, meant to warn us if we
+ // start creating too many objects. It can safely be raised to a higher
+ // level, and the program is designed to handle much larger values.
+ // Before raising this limit, make sure that there is a very good reason
+ // (in your debug testing) to be watching this many objects.
+ DCHECK(2 <= warning_multiple);
+ ++warning_multiple;
+ }
+ objects_.push_back(object);
+ watchers_.push_back(watcher);
+ } else {
+ watchers_[it - objects_.begin()] = watcher;
+ }
+ } else if (it != objects_.end()) {
+ std::vector<HANDLE>::difference_type index = it - objects_.begin();
+ objects_.erase(it);
+ watchers_.erase(watchers_.begin() + index);
+ }
+ return true;
+}
+
+// Possibly called on a background thread!
+void MessageLoop::PostDelayedTask(const tracked_objects::Location& from_here,
+ Task* task, int delay_ms) {
+ task->SetBirthPlace(from_here);
+ DCHECK(delay_ms >= 0);
+ DCHECK(!task->is_owned_by_message_loop());
+ task->set_posted_task_delay(delay_ms);
+ DCHECK(task->is_owned_by_message_loop());
+ PostTaskInternal(task);
+}
+
+void MessageLoop::PostTaskInternal(Task* task) {
+ // Warning: Don't try to short-circuit, and handle this thread's tasks more
+ // directly, as it could starve handling of foreign threads. Put every task
+ // into this queue.
+
+ // Local stack variables to use IF we need to process after releasing locks.
+ HWND message_hwnd;
+ {
+ AutoLock lock1(incoming_queue_lock_);
+ bool was_empty = incoming_queue_.Empty();
+ incoming_queue_.Push(task);
+ if (!was_empty)
+ return; // Someone else should have started the sub-pump.
+
+ // We may have to start the sub-pump.
+ AutoLock lock2(task_pump_message_lock_);
+ if (task_pump_message_pending_)
+ return; // Someone else continued the pumping.
+ task_pump_message_pending_ = true; // We'll send one.
+ message_hwnd = message_hwnd_;
+ } // Release both locks.
+ // We may have just posted a kMsgQuit, and so this instance may now destroyed!
+ // Do not invoke non-static methods, or members in any way!
+
+ // PostMessage may fail, as the hwnd may have vanished due to kMsgQuit.
+ PostMessage(message_hwnd, kMsgPumpATask, reinterpret_cast<UINT_PTR>(this), 0);
+}
+
+void MessageLoop::InitMessageWnd() {
+ HINSTANCE hinst = GetModuleHandle(NULL);
+
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ wc.lpfnWndProc = MessageLoopWndProc;
+ wc.hInstance = hinst;
+ wc.lpszClassName = kWndClass;
+ RegisterClassEx(&wc);
+
+ message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+ hinst, 0);
+ DCHECK(message_hwnd_);
+}
+
+LRESULT MessageLoop::MessageWndProc(HWND hwnd, UINT message,
+ WPARAM wparam, LPARAM lparam) {
+ DCHECK(hwnd == message_hwnd_);
+ switch (message) {
+ case kMsgPumpATask: {
+ ProcessPumpReplacementMessage(); // Avoid starving paint and timer.
+ if (!nestable_tasks_allowed_)
+ return 0;
+ PumpATaskDuringWndProc();
+ return 0;
+ }
+
+ case kMsgQuit: {
+ quit_received_ = true;
+ return 0;
+ }
+ }
+ return ::DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+void MessageLoop::WillProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg));
+}
+
+void MessageLoop::DidProcessMessage(const MSG& msg) {
+ FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg));
+}
+
+void MessageLoop::SetNestableTasksAllowed(bool allowed) {
+ nestable_tasks_allowed_ = allowed;
+ if (!nestable_tasks_allowed_)
+ return;
+ // Start the native pump if we are not already pumping.
+ EnsurePumpATaskWasPosted();
+}
+
+bool MessageLoop::NestableTasksAllowed() const {
+ return nestable_tasks_allowed_;
+}
+
+
+bool MessageLoop::ProcessNextWindowsMessage() {
+ MSG msg;
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ return ProcessMessageHelper(msg);
+ }
+ return false;
+}
+
+bool MessageLoop::ProcessMessageHelper(const MSG& msg) {
+ HistogramEvent(msg.message);
+
+ if (WM_QUIT == msg.message) {
+ // Repost the QUIT message so that it will be retrieved by the primary
+ // GetMessage() loop.
+ quit_now_ = true;
+ PostQuitMessage(static_cast<int>(msg.wParam));
+ return false;
+ }
+
+ // While running our main message pump, we discard kMsgPumpATask messages.
+ if (msg.message == kMsgPumpATask && msg.hwnd == message_hwnd_)
+ return ProcessPumpReplacementMessage();
+
+ WillProcessMessage(msg);
+
+ if (dispatcher_) {
+ if (!dispatcher_->Dispatch(msg))
+ quit_now_ = true;
+ } else {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DidProcessMessage(msg);
+ return true;
+}
+
+bool MessageLoop::ProcessPumpReplacementMessage() {
+ MSG msg;
+ bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE));
+ DCHECK(!have_message || kMsgPumpATask != msg.message
+ || msg.hwnd != message_hwnd_);
+ {
+ // Since we discarded a kMsgPumpATask message, we must update the flag.
+ AutoLock lock(task_pump_message_lock_);
+ DCHECK(task_pump_message_pending_);
+ task_pump_message_pending_ = false;
+ }
+ return have_message && ProcessMessageHelper(msg);
+}
+
+// Create a mini-message-pump to force immediate processing of only Windows
+// WM_PAINT messages.
+void MessageLoop::PumpOutPendingPaintMessages() {
+ // 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;
+ bool win2k(true);
+ if (win_util::GetWinVersion() > win_util::WINVERSION_2000)
+ win2k = false;
+ for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) {
+ MSG msg;
+ if (win2k) {
+ if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE))
+ break;
+ } else {
+ if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT))
+ break;
+ }
+ ProcessMessageHelper(msg);
+ if (quit_now_ ) // Handle WM_QUIT.
+ break;
+ }
+ // Histogram what was really being used, to help to adjust kMaxPeekCount.
+ DHISTOGRAM_COUNTS(L"Loop.PumpOutPendingPaintMessages Peeks", peek_count);
+}
+
+//------------------------------------------------------------------------------
+// If we handle more than the OS limit on the number of objects that can be
+// waited for, we'll need to poll (sequencing through subsets of the objects
+// that can be passed in a single OS wait call). The following is the polling
+// interval used in that (unusual) case. (I don't have a lot of justifcation
+// for the specific value, but it needed to be short enough that it would not
+// add a lot of latency, and long enough that we wouldn't thrash the CPU for no
+// reason... especially considering the silly user probably has a million tabs
+// open, etc.)
+static const int kMultipleWaitPollingInterval = 20;
+
+void MessageLoop::WaitForWork() {
+ bool original_can_run = nestable_tasks_allowed_;
+ int wait_flags = original_can_run ? MWMO_ALERTABLE | MWMO_INPUTAVAILABLE
+ : MWMO_INPUTAVAILABLE;
+
+ bool use_polling = false; // Poll if too many objects for one OS Wait call.
+ for (;;) {
+ // Do initialization here, in case APC modifies object list.
+ size_t total_objs = original_can_run ? objects_.size() : 0;
+
+ int delay;
+ size_t polling_index = 0; // The first unprocessed object index.
+ do {
+ size_t objs_len =
+ (polling_index < total_objs) ? total_objs - polling_index : 0;
+ if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS) {
+ objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
+ use_polling = true;
+ }
+ HANDLE* objs = objs_len ? polling_index + &objects_.front() : NULL;
+
+ // Only wait up to the time needed by the timer manager to fire the next
+ // set of timers.
+ delay = timer_manager_.GetCurrentDelay();
+ if (use_polling && delay > kMultipleWaitPollingInterval)
+ delay = kMultipleWaitPollingInterval;
+ if (delay < 0) // Negative value means no timers waiting.
+ delay = INFINITE;
+
+ DWORD result;
+ result = MsgWaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
+ delay, QS_ALLINPUT, wait_flags);
+
+ if (WAIT_IO_COMPLETION == result) {
+ HistogramEvent(kSleepingApcEvent);
+ // We'll loop here when we service an APC. At it currently stands,
+ // *ONLY* the IO thread uses *any* APCs, so this should have no impact
+ // on the UI thread.
+ break; // Break to outer loop, and waitforwork() again.
+ }
+
+ // Use unsigned type to simplify range detection;
+ size_t signaled_index = result - WAIT_OBJECT_0;
+ if (signaled_index < objs_len) {
+ SignalWatcher(polling_index + signaled_index);
+ HistogramEvent(kSleepingSignalEvent);
+ return; // We serviced a signaled object.
+ }
+
+ if (objs_len == signaled_index)
+ return; // A WM_* message is available.
+
+ DCHECK_NE(WAIT_FAILED, result) << GetLastError();
+
+ DCHECK(!objs || result == WAIT_TIMEOUT);
+ if (!use_polling)
+ return;
+ polling_index += objs_len;
+ } while (polling_index < total_objs);
+ // For compatibility, we didn't return sooner. This made us do *some* wait
+ // call(s) before returning. This will probably change in next rev.
+ if (!delay || !timer_manager_.GetCurrentDelay())
+ return; // No work done, but timer is ready to fire.
+ }
+}
+
+// Note: MsgWaitMultipleObjects() can't take a nil list, and that is why I had
+// to use SleepEx() to handle APCs when there were no objects.
+bool MessageLoop::ProcessNextObject() {
+ if (!nestable_tasks_allowed_)
+ return false;
+
+ size_t total_objs = objects_.size();
+ if (!total_objs) {
+ return false;
+ }
+
+ size_t polling_index = 0; // The first unprocessed object index.
+ do {
+ DCHECK(polling_index < total_objs);
+ size_t objs_len = total_objs - polling_index;
+ if (objs_len >= CHROME_MAXIMUM_WAIT_OBJECTS)
+ objs_len = CHROME_MAXIMUM_WAIT_OBJECTS - 1;
+ HANDLE* objs = polling_index + &objects_.front();
+
+ // Identify 1 pending object, or allow an IO APC to be completed.
+ DWORD result = WaitForMultipleObjectsEx(static_cast<DWORD>(objs_len), objs,
+ FALSE, // 1 signal is sufficient.
+ 0, // Wait 0ms.
+ false); // Not alertable (no APC).
+
+ // Use unsigned type to simplify range detection;
+ size_t signaled_index = result - WAIT_OBJECT_0;
+ if (signaled_index < objs_len) {
+ SignalWatcher(polling_index + signaled_index);
+ HistogramEvent(kPollingSignalEvent);
+ return true; // We serviced a signaled object.
+ }
+
+ // If an handle is invalid, it will be WAIT_FAILED.
+ DCHECK_EQ(WAIT_TIMEOUT, result) << GetLastError();
+ polling_index += objs_len;
+ } while (polling_index < total_objs);
+ return false; // We serviced nothing.
+}
+
+bool MessageLoop::SignalWatcher(size_t object_index) {
+ BeforeTaskRunSetup();
+ DCHECK(objects_.size() > object_index);
+ // On reception of OnObjectSignaled() to a Watcher object, it may call
+ // WatchObject(). watchers_ and objects_ will be modified. This is
+ // expected, so don't be afraid if, while tracing a OnObjectSignaled()
+ // function, the corresponding watchers_[result] is inexistant.
+ watchers_[object_index]->OnObjectSignaled(objects_[object_index]);
+ // Signaled objects tend to be removed from the watch list, and then added
+ // back (appended). As a result, they move to the end of the objects_ array,
+ // and this should make their service "fair" (no HANDLEs should be starved).
+ AfterTaskRunRestore();
+ return true;
+}
+
+bool MessageLoop::RunTimerTask(Timer* timer) {
+ HistogramEvent(kTimerEvent);
+ Task* task = timer->task();
+ if (task->is_owned_by_message_loop()) {
+ // We constructed it through PostTask().
+ DCHECK(!timer->repeating());
+ timer->set_task(NULL);
+ delete timer;
+ return QueueOrRunTask(task);
+ } else {
+ // This is an unknown timer task, and we *can't* delay running it, as a
+ // user might try to cancel it with TimerManager at any moment.
+ DCHECK(nestable_tasks_allowed_);
+ RunTask(task);
+ return true;
+ }
+}
+
+void MessageLoop::DiscardTimer(Timer* timer) {
+ Task* task = timer->task();
+ if (task->is_owned_by_message_loop()) {
+ DCHECK(!timer->repeating());
+ timer->set_task(NULL);
+ delete timer; // We constructed it through PostDelayedTask().
+ delete task; // We were given ouwnership in PostTask().
+ }
+}
+
+bool MessageLoop::QueueOrRunTask(Task* new_task) {
+ if (!nestable_tasks_allowed_) {
+ // Task can't be executed right now. Add it to the queue.
+ if (new_task)
+ work_queue_.Push(new_task);
+ return false;
+ }
+
+ // Queue new_task first so we execute the task in FIFO order.
+ if (new_task)
+ work_queue_.Push(new_task);
+
+ // Execute oldest task.
+ while (!work_queue_.Empty()) {
+ Task* task = work_queue_.Pop();
+ if (task->nestable() || run_depth_ == 1) {
+ RunTask(task);
+ // Show that we ran a task (Note: a new one might arrive as a
+ // consequence!).
+ return true;
+ } else {
+ // We couldn't run the task now because we're in a nested message loop
+ // and the task isn't nestable.
+ delayed_non_nestable_queue_.Push(task);
+ }
+ }
+
+ // Nothing happened.
+ return false;
+}
+
+void MessageLoop::RunTask(Task* task) {
+ BeforeTaskRunSetup();
+ HistogramEvent(kTaskRunEvent);
+ // task may self-delete during Run() if we don't happen to own it.
+ // ...so check *before* we Run, since we can't check after.
+ bool we_own_task = task->is_owned_by_message_loop();
+ task->Run();
+ if (we_own_task)
+ task->RecycleOrDelete(); // Relinquish control, and probably delete.
+ AfterTaskRunRestore();
+}
+
+void MessageLoop::BeforeTaskRunSetup() {
+ DCHECK(nestable_tasks_allowed_);
+ // Execute the task and assume the worst: It is probably not reentrant.
+ nestable_tasks_allowed_ = false;
+}
+
+void MessageLoop::AfterTaskRunRestore() {
+ nestable_tasks_allowed_ = true;
+}
+
+void MessageLoop::PumpATaskDuringWndProc() {
+ // TODO(jar): Perchance we should check on signaled objects here??
+ // Signals are generally starved during a native message loop. Even if we
+ // try to service a signaled object now, we wouldn't automatically get here
+ // (i.e., the native pump would not re-start) when the next object was
+ // signaled. If we really want to avoid starving signaled objects, we need
+ // to translate them into Tasks that can be passed in via PostTask.
+ // If these native message loops (and sub-pumping activities) are short
+ // lived, then the starvation won't be that long :-/.
+
+ if (!ProcessNextDeferredTask())
+ return; // Nothing to do, so lets stop the sub-pump.
+
+ // We ran a task, so make sure we come back and try to run more tasks.
+ EnsurePumpATaskWasPosted();
+}
+
+void MessageLoop::EnsurePumpATaskWasPosted() {
+ {
+ AutoLock lock(task_pump_message_lock_);
+ if (task_pump_message_pending_)
+ return; // Someone else continued the pumping.
+ task_pump_message_pending_ = true; // We'll send one.
+ }
+ EnsureMessageGetsPosted(kMsgPumpATask);
+}
+
+void MessageLoop::EnsureMessageGetsPosted(int message) const {
+ const int kRetryCount = 30;
+ const int kSleepDurationWhenFailing = 100;
+ for (int i = 0; i < kRetryCount; ++i) {
+ // Posting to our own windows should always succeed. If it doesn't we're in
+ // big trouble.
+ if (PostMessage(message_hwnd_, message,
+ reinterpret_cast<UINT_PTR>(this), 0))
+ return;
+ Sleep(kSleepDurationWhenFailing);
+ }
+ LOG(FATAL) << "Crash with last error " << GetLastError();
+ int* p = NULL;
+ *p = 0; // Crash.
+}
+
+void MessageLoop::ReloadWorkQueue() {
+ // We can improve performance of our loading tasks from incoming_queue_ to
+ // work_queue_ by wating until the last minute (work_queue_ is empty) to load.
+ // That reduces the number of locks-per-task significantly when our queues get
+ // large. The optimization is disabled on threads that make use of the
+ // priority queue (prioritization requires all our tasks to be in the
+ // work_queue_ ASAP).
+ if (!work_queue_.Empty() && !work_queue_.use_priority_queue())
+ return; // Wait till we *really* need to lock and load.
+
+ // Acquire all we can from the inter-thread queue with one lock acquisition.
+ TaskQueue new_task_list; // Null terminated list.
+ {
+ AutoLock lock(incoming_queue_lock_);
+ if (incoming_queue_.Empty())
+ return;
+ std::swap(incoming_queue_, new_task_list);
+ DCHECK(incoming_queue_.Empty());
+ } // Release lock.
+
+ while (!new_task_list.Empty()) {
+ Task* task = new_task_list.Pop();
+ DCHECK(task->is_owned_by_message_loop());
+
+ if (task->posted_task_delay() > 0)
+ timer_manager_.StartTimer(task->posted_task_delay(), task, false);
+ else
+ work_queue_.Push(task);
+ }
+}
+
+void MessageLoop::DeletePendingTasks() {
+ /* Comment this out as it's causing crashes.
+ while (!work_queue_.Empty()) {
+ Task* task = work_queue_.Pop();
+ if (task->is_owned_by_message_loop())
+ delete task;
+ }
+
+ while (!delayed_non_nestable_queue_.Empty()) {
+ Task* task = delayed_non_nestable_queue_.Pop();
+ if (task->is_owned_by_message_loop())
+ delete task;
+ }
+ */
+}
+
+//------------------------------------------------------------------------------
+// Implementation of the work_queue_ as a ProiritizedTaskQueue
+
+void MessageLoop::PrioritizedTaskQueue::push(Task * task) {
+ queue_.push(PrioritizedTask(task, --next_sequence_number_));
+}
+
+bool MessageLoop::PrioritizedTaskQueue::PrioritizedTask::operator < (
+ PrioritizedTask const & right) const {
+ int compare = task_->priority_ - right.task_->priority_;
+ if (compare)
+ return compare < 0;
+ // Don't compare directly, but rather subtract. This handles overflow
+ // as sequence numbers wrap around.
+ compare = sequence_number_ - right.sequence_number_;
+ DCHECK(compare); // Sequence number are unique for a "long time."
+ // Make sure we don't starve anything with a low priority.
+ CHECK(INT_MAX/8 > compare); // We don't get close to wrapping.
+ CHECK(INT_MIN/8 < compare); // We don't get close to wrapping.
+ return compare < 0;
+}
+
+//------------------------------------------------------------------------------
+// Implementation of a TaskQueue as a null terminated list, with end pointers.
+
+void MessageLoop::TaskQueue::Push(Task* task) {
+ if (!first_)
+ first_ = task;
+ else
+ last_->set_next_task(task);
+ last_ = task;
+}
+
+Task* MessageLoop::TaskQueue::Pop() {
+ DCHECK((!first_) == !last_);
+ Task* task = first_;
+ if (first_) {
+ first_ = task->next_task();
+ if (!first_)
+ last_ = NULL;
+ else
+ task->set_next_task(NULL);
+ }
+ return task;
+}
+
+//------------------------------------------------------------------------------
+// Implementation of a Task queue that automatically switches into a priority
+// queue if it observes any non-zero priorities on tasks.
+
+void MessageLoop::OptionallyPrioritizedTaskQueue::Push(Task* task) {
+ if (use_priority_queue_) {
+ prioritized_queue_.push(task);
+ } else {
+ queue_.Push(task);
+ if (task->priority()) {
+ use_priority_queue_ = true; // From now on.
+ while (!queue_.Empty())
+ prioritized_queue_.push(queue_.Pop());
+ }
+ }
+}
+
+Task* MessageLoop::OptionallyPrioritizedTaskQueue::Pop() {
+ if (!use_priority_queue_)
+ return queue_.Pop();
+ Task* task = prioritized_queue_.front();
+ prioritized_queue_.pop();
+ return task;
+}
+
+bool MessageLoop::OptionallyPrioritizedTaskQueue::Empty() {
+ if (use_priority_queue_)
+ return prioritized_queue_.empty();
+ return queue_.Empty();
+}
+
+//------------------------------------------------------------------------------
+// Method and data for histogramming events and actions taken by each instance
+// on each thread.
+
+// static
+bool MessageLoop::enable_histogrammer_ = false;
+
+// static
+void MessageLoop::EnableHistogrammer(bool enable) {
+ enable_histogrammer_ = enable;
+}
+
+void MessageLoop::StartHistogrammer() {
+ if (enable_histogrammer_ && !message_histogram_.get()
+ && StatisticsRecorder::WasStarted()) {
+ message_histogram_.reset(new LinearHistogram(
+ ASCIIToWide("MsgLoop:" + thread_name_).c_str(),
+ kLeastNonZeroMessageId,
+ kMaxMessageId,
+ kNumberOfDistinctMessagesDisplayed));
+ message_histogram_->SetFlags(message_histogram_->kHexRangePrintingFlag);
+ message_histogram_->SetRangeDescriptions(event_descriptions_);
+ }
+}
+
+void MessageLoop::HistogramEvent(int event) {
+ if (message_histogram_.get())
+ message_histogram_->Add(event);
+}
+
+// Add one undocumented windows message to clean up our display.
+#ifndef WM_SYSTIMER
+#define WM_SYSTIMER 0x118
+#endif
+
+// Provide a macro that takes an expression (such as a constant, or macro
+// constant) and creates a pair to initalize an array of pairs. In this case,
+// our pair consists of the expressions value, and the "stringized" version
+// of the expression (i.e., the exrpression put in quotes). For example, if
+// we have:
+// #define FOO 2
+// #define BAR 5
+// then the following:
+// VALUE_TO_NUMBER_AND_NAME(FOO + BAR)
+// will expand to:
+// {7, "FOO + BAR"}
+// We use the resulting array as an argument to our histogram, which reads the
+// number as a bucket identifier, and proceeds to use the corresponding name
+// in the pair (i.e., the quoted string) when printing out a histogram.
+#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name},
+
+
+// static
+const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = {
+ // Only provide an extensive list in debug mode. In release mode, we have to
+ // read the octal values.... but we save about 450 strings, each of length
+ // 10 from our binary image.
+#ifndef NDEBUG
+ // Prepare to include a list of names provided in a special header file4.
+#define A_NAMED_MESSAGE_FROM_WINUSER_H VALUE_TO_NUMBER_AND_NAME
+#include "base/windows_message_list.h"
+#undef A_NAMED_MESSAGE_FROM_WINUSER_H
+ // Add an undocumented message that appeared in our list :-/.
+ VALUE_TO_NUMBER_AND_NAME(WM_SYSTIMER)
+#endif // NDEBUG
+
+ // Provide some pretty print capability in our histogram for our internal
+ // messages.
+
+ // Values we use for WM_USER+n
+ VALUE_TO_NUMBER_AND_NAME(kMsgPumpATask)
+ VALUE_TO_NUMBER_AND_NAME(kMsgQuit)
+
+ // A few events we handle (kindred to messages), and used to profile actions.
+ VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent)
+ VALUE_TO_NUMBER_AND_NAME(kSleepingApcEvent)
+ VALUE_TO_NUMBER_AND_NAME(kSleepingSignalEvent)
+ VALUE_TO_NUMBER_AND_NAME(kPollingSignalEvent)
+ VALUE_TO_NUMBER_AND_NAME(kTimerEvent)
+
+ {-1, NULL} // The list must be null terminated, per API to histogram.
+};
diff --git a/base/message_loop.h b/base/message_loop.h
new file mode 100644
index 0000000..6bb7395
--- /dev/null
+++ b/base/message_loop.h
@@ -0,0 +1,611 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MESSAGE_LOOP_H__
+#define BASE_MESSAGE_LOOP_H__
+
+#include <windows.h>
+#include <deque>
+#include <queue>
+#include <string>
+#include <vector>
+
+#include "base/histogram.h"
+#include "base/observer_list.h"
+#include "base/id_map.h"
+#include "base/task.h"
+#include "base/timer.h"
+#include "base/thread_local_storage.h"
+
+//
+// A MessageLoop is used to process events for a particular thread.
+// There is at most one MessageLoop instance per thread.
+// Events include Windows Message Queue messages, Tasks submitted to PostTask
+// or managed by TimerManager, APC calls (as time permits), and signals sent to
+// a registered set of HANDLES.
+// Processing events corresponds (respectively) to dispatching Windows messages,
+// running Tasks, yielding time to APCs, and calling Watchers when the
+// corresponding HANDLE is signaled.
+
+//
+// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
+// on the thread where the MessageLoop's Run method executes.
+//
+// WARNING: MessageLoop has task reentrancy protection. This means that if a
+// task is being processed, a second task cannot start until the first task is
+// finished. Reentrancy can happen when processing a task, and an inner message
+// pump is created. That inner pump then processes windows messages which could
+// implicitly start an inner task. Inner messages pumps are created with dialogs
+// (DialogBox), common dialogs (GetOpenFileName), OLE functions (DoDragDrop),
+// printer functions (StartDoc) and *many* others.
+// Sample workaround when inner task processing is needed:
+// bool old_state = MessageLoop::current()->NestableTasksAllowed();
+// MessageLoop::current()->SetNestableTasksAllowed(true);
+// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here.
+// MessageLoop::current()->SetNestableTasksAllowed(old_state);
+// // Process hr (the result returned by DoDragDrop().
+//
+// Please be **SURE** your task is reentrant and all global variables are stable
+// and accessible before calling SetNestableTasksAllowed(true).
+//
+
+// Message loop has several distinct functions. It provides message pumps,
+// responds to windows message dispatches, manipulates queues of Tasks.
+// The most central operation is the implementation of message pumps, along with
+// several subtleties.
+
+// MessageLoop currently implements several different message pumps. A message
+// pump is (traditionally) something that reads from an incoming queue, and then
+// dispatches the work.
+//
+// The first message pump, RunTraditional(), is among other things a
+// traditional Windows Message pump. It contains a nearly infinite loop that
+// peeks out messages, and then dispatches them.
+// Intermixed with those peeks are checks on a queue of Tasks, checks for
+// signaled objects, and checks to see if TimerManager has tasks to run.
+// When there are no events to be serviced, this pump goes into a wait state.
+// For 99.99% of all events, this first message pump handles all processing.
+//
+// When a task, or windows event, invokes on the stack a native dialog box or
+// such, that window typically provides a bare bones (native?) message pump.
+// That bare-bones message pump generally supports little more than a peek of
+// the Windows message queue, followed by a dispatch of the peeked message.
+// MessageLoop extends that bare-bones message pump to also service Tasks, at
+// the cost of some complexity.
+// The basic structure of the extension (refered to as a sub-pump) is that a
+// special message,kMsgPumpATask, is repeatedly injected into the Windows
+// Message queue. Each time the kMsgPumpATask message is peeked, checks are made
+// for an extended set of events, including the availability of Tasks to run.
+//
+// After running a task, the special message kMsgPumpATask is again posted to
+// the Windows Message queue, ensuring a future time slice for processing a
+// future event.
+//
+// To prevent flooding the Windows Message queue, care is taken to be sure that
+// at most one kMsgPumpATask message is EVER pending in the Winow's Message
+// queue.
+//
+// There are a few additional complexities in this system where, when there are
+// no Tasks to run, this otherwise infinite stream of messages which drives the
+// sub-pump is halted. The pump is automatically re-started when Tasks are
+// queued.
+//
+// A second complexity is that the presence of this stream of posted tasks may
+// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
+// Such paint and timer events always give priority to a posted message, such as
+// kMsgPumpATask messages. As a result, care is taken to do some peeking in
+// between the posting of each kMsgPumpATask message (i.e., after kMsgPumpATask
+// is peeked, and before a replacement kMsgPumpATask is posted).
+//
+//
+// NOTE: Although it may seem odd that messages are used to start and stop this
+// flow (as opposed to signaling objects, etc.), it should be understood that
+// the native message pump will *only* respond to messages. As a result, it is
+// an excellent choice. It is also helpful that the starter messages that are
+// placed in the queue when new task arrive also awakens the RunTraditional()
+// loop.
+
+//------------------------------------------------------------------------------
+// Define a macro to record where (in the sourec code) each Task is posted from.
+#define FROM_HERE tracked_objects::Location(__FUNCTION__, __FILE__, __LINE__)
+
+//------------------------------------------------------------------------------
+class MessageLoop {
+ public:
+
+ // Select a non-default strategy for serving pending requests, that is to be
+ // used by all MessageLoop instances. This is called only once before
+ // constructing any instances.
+ static void SetStrategy(int strategy);
+ static void EnableHistogrammer(bool enable_histogrammer);
+
+ // Used with WatchObject to asynchronously monitor the signaled state of a
+ // HANDLE object.
+ class Watcher {
+ public:
+ virtual ~Watcher() {}
+ // Called from MessageLoop::Run when a signalled object is detected.
+ virtual void OnObjectSignaled(HANDLE object) = 0;
+ };
+
+ // Dispatcher is used during a nested invocation of Run to dispatch events.
+ // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
+ // dispatch events (or invoke TranslateMessage), rather every message is
+ // passed to Dispatcher's Dispatch method for dispatch. It is up to the
+ // Dispatcher to dispatch, or not, the event.
+ //
+ // The nested loop is exited by either posting a quit, or returning false
+ // from Dispatch.
+ class Dispatcher {
+ public:
+ // Define a macro for use in the PostTask() or PostDelayedTask()
+ // invocations. The definition varies depending upon mode (DEBUG, etc.),
+ // but for now we'll just define it as an int. In other modes it may
+ // encapsulate the file and line number of the source code where it is
+ // expanded.
+
+ virtual ~Dispatcher() {}
+ // Dispatches the event. If true is returned processing continues as
+ // normal. If false is returned, the nested loop exits immediately.
+ virtual bool Dispatch(const MSG& msg) = 0;
+ };
+
+ // Have the current thread's message loop watch for a signaled object.
+ // Pass a null watcher to stop watching the object.
+ bool WatchObject(HANDLE, Watcher*);
+
+ // An Observer is an object that receives global notifications from the
+ // MessageLoop.
+ //
+ // NOTE: An Observer implementation should be extremely fast!
+ //
+ class Observer {
+ public:
+ virtual ~Observer() {}
+
+ // This method is called before processing a message.
+ // The message may be undefined in which case msg.message is 0
+ virtual void WillProcessMessage(const MSG& msg) = 0;
+
+ // This method is called when control returns from processing a UI message.
+ // The message may be undefined in which case msg.message is 0
+ virtual void DidProcessMessage(const MSG& msg) = 0;
+ };
+
+ // Add an Observer, which will start receiving notifications immediately.
+ void AddObserver(Observer* observer);
+
+ // Remove an Observer. It is safe to call this method while an Observer is
+ // receiving a notification callback.
+ void RemoveObserver(Observer* observer);
+
+ // Call the task's Run method asynchronously from within a message loop at
+ // some point in the future. With the PostTask variant, tasks are invoked in
+ // FIFO order, inter-mixed with normal UI event processing. With the
+ // PostDelayedTask variant, tasks are called after at least approximately
+ // 'delay_ms' have elapsed.
+ //
+ // The MessageLoop takes ownership of the Task, and deletes it after it
+ // has been Run().
+ //
+ // NOTE: This method may be called on any thread. The Task will be invoked
+ // on the thread that executes MessageLoop::Run().
+
+ void PostTask(const tracked_objects::Location& from_here, Task* task) {
+ PostDelayedTask(from_here, task, 0);
+ }
+
+ void PostDelayedTask(const tracked_objects::Location& from_here, Task* task,
+ int delay_ms);
+
+ // A variant on PostTask that deletes the given object. This is useful
+ // if the object needs to live until the next run of the MessageLoop (for
+ // example, deleting a RenderProcessHost from within an IPC callback is not
+ // good).
+ //
+ // NOTE: This method may be called on any thread. The object will be deleted
+ // on the thread that executes MessageLoop::Run(). If this is not the same
+ // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit
+ // from RefCountedThreadSafe<T>!
+ template <class T>
+ void DeleteSoon(const tracked_objects::Location& from_here, T* object) {
+ PostTask(from_here, new DeleteTask<T>(object));
+ }
+
+ // A variant on PostTask that releases the given reference counted object
+ // (by calling its Release method). This is useful if the object needs to
+ // live until the next run of the MessageLoop, or if the object needs to be
+ // released on a particular thread.
+ //
+ // NOTE: This method may be called on any thread. The object will be
+ // released (and thus possibly deleted) on the thread that executes
+ // MessageLoop::Run(). If this is not the same as the thread that calls
+ // PostDelayedTask(FROM_HERE, ), then T MUST inherit from
+ // RefCountedThreadSafe<T>!
+ template <class T>
+ void ReleaseSoon(const tracked_objects::Location& from_here, T* object) {
+ PostTask(from_here, new ReleaseTask<T>(object));
+ }
+
+ // Run the message loop
+ void Run();
+
+ // See description of Dispatcher for how Run uses Dispatcher.
+ void Run(Dispatcher* dispatcher);
+
+ // Signals the Run method to return after it is done processing all pending
+ // messages. This method may be called from any thread, but no effort is
+ // made to support concurrent calls to this method from multiple threads.
+ //
+ // For example, the first call to Quit may lead to the MessageLoop being
+ // deleted once its Run method returns, so a second call from another thread
+ // could be problematic.
+ void Quit();
+
+ // Invokes Quit on the current MessageLoop when run. Useful to schedule an
+ // arbitrary MessageLoop to Quit.
+ class QuitTask : public Task {
+ public:
+ virtual void Run() {
+ MessageLoop::current()->Quit();
+ }
+ };
+
+ // Wnd Proc for message_hwnd_.
+ LRESULT MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ // Normally, it is not necessary to instantiate a MessageLoop. Instead, it
+ // is typical to make use of the current thread's MessageLoop instance.
+ MessageLoop();
+ ~MessageLoop();
+
+ // Optional call to connect the thread name with this loop.
+ void SetThreadName(const std::string& thread_name);
+ std::string thread_name() const { return thread_name_; }
+
+ // Returns the MessageLoop object for the current thread, or null if none.
+ static MessageLoop* current() {
+ return static_cast<MessageLoop*>(ThreadLocalStorage::Get(tls_index_));
+ }
+
+ // Returns the TimerManager object for the current thread.
+ TimerManager* timer_manager() { return &timer_manager_; }
+
+ // Give a chance to code processing additional messages to notify the
+ // message loop delegates that another message has been processed.
+ void WillProcessMessage(const MSG& msg);
+ void DidProcessMessage(const MSG& msg);
+
+ // Enables or disables the recursive task processing. This happens in the case
+ // of recursive message loops. Some unwanted message loop may occurs when
+ // using common controls or printer functions. By default, recursive task
+ // processing is disabled.
+ //
+ // The specific case where tasks get queued is:
+ // - The thread is running a message loop.
+ // - It receives a task #1 and execute it.
+ // - The task #1 implicitly start a message loop, like a MessageBox in the
+ // unit test. This can also be StartDoc or GetSaveFileName.
+ // - The thread receives a task #2 before or while in this second message
+ // loop.
+ // - With NestableTasksAllowed set to true, the task #2 will run right away.
+ // Otherwise, it will get executed right after task #1 completes at "thread
+ // message loop level".
+ void SetNestableTasksAllowed(bool allowed);
+ bool NestableTasksAllowed() const;
+
+ // Enables or disables the restoration during an exception of the unhandled
+ // exception filter that was active when Run() was called. This can happen
+ // if some third party code call SetUnhandledExceptionFilter() and never
+ // restores the previous filter.
+ void set_exception_restoration(bool restore) {
+ exception_restoration_ = restore;
+ }
+
+ // Public entry point for TimerManager to request the Run() of a task. If we
+ // created the task during an PostTask(FROM_HERE, ), then we will also perform
+ // destructions, and we'll have the option of queueing the task. If we didn't
+ // create the timer, then we will Run it immediately.
+ bool RunTimerTask(Timer* timer);
+
+ // Since some Timer's are owned by MessageLoop, the TimerManager (when it is
+ // being destructed) passses us the timers to discard (without doing a Run()).
+ void DiscardTimer(Timer* timer);
+
+ // 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:
+ struct ScopedStateSave {
+ explicit ScopedStateSave(MessageLoop* loop)
+ : loop_(loop),
+ dispatcher_(loop->dispatcher_),
+ quit_now_(loop->quit_now_),
+ quit_received_(loop->quit_received_) {
+ loop->quit_now_ = loop->quit_received_ = false;
+ }
+
+ ~ScopedStateSave() {
+ loop_->quit_received_ = quit_received_;
+ loop_->quit_now_ = quit_now_;
+ loop_->dispatcher_ = dispatcher_;
+ }
+
+ private:
+ MessageLoop* loop_;
+ Dispatcher* dispatcher_;
+ bool quit_now_;
+ bool quit_received_;
+ }; // struct ScopedStateSave
+
+ // A prioritized queue with interface that mostly matches std::queue<>.
+ // For debugging/performance testing, you can swap in std::queue<Task*>.
+ class PrioritizedTaskQueue {
+ public:
+ PrioritizedTaskQueue() : next_sequence_number_(0) {}
+ ~PrioritizedTaskQueue() {}
+ void pop() { queue_.pop(); }
+ bool empty() { return queue_.empty(); }
+ size_t size() { return queue_.size(); }
+ Task* front() { return queue_.top().task(); }
+ void push(Task * task);
+
+ private:
+ class PrioritizedTask {
+ public:
+ PrioritizedTask(Task* task, int sequence_number)
+ : task_(task),
+ sequence_number_(sequence_number),
+ priority_(task->priority()) {}
+ Task* task() { return task_; }
+ bool operator < (PrioritizedTask const & right) const ;
+
+ private:
+ Task* task_;
+ // Number to ensure (default) FIFO ordering in a PriorityQueue.
+ int sequence_number_;
+ // Priority of task when pushed.
+ int priority_;
+ }; // class PrioritizedTask
+
+ std::priority_queue<PrioritizedTask> queue_;
+ // Default sequence number used when push'ing (monotonically decreasing).
+ int next_sequence_number_;
+ DISALLOW_EVIL_CONSTRUCTORS(PrioritizedTaskQueue);
+ };
+
+ // Implementation of a TaskQueue as a null terminated list, with end pointers.
+ class TaskQueue {
+ public:
+ TaskQueue() : first_(NULL), last_(NULL) {}
+ void Push(Task* task);
+ Task* Pop(); // Extract the next Task from the queue, and return it.
+ bool Empty() const { return !first_; }
+ friend void std::swap<TaskQueue>(TaskQueue&, TaskQueue&);
+ private:
+ Task* first_;
+ Task* last_;
+ };
+
+ // Implementation of a Task queue that automatically switches into a priority
+ // queue if it observes any non-zero priorities in tasks.
+ class OptionallyPrioritizedTaskQueue {
+ public:
+ OptionallyPrioritizedTaskQueue() : use_priority_queue_(false) {}
+ void Push(Task* task);
+ Task* Pop(); // Extract next Task from queue, and return it.
+ bool Empty();
+ bool use_priority_queue() const { return use_priority_queue_; }
+
+ private:
+ bool use_priority_queue_;
+ PrioritizedTaskQueue prioritized_queue_;
+ TaskQueue queue_;
+ DISALLOW_EVIL_CONSTRUCTORS(OptionallyPrioritizedTaskQueue);
+ };
+
+ void InitMessageWnd();
+
+ // The actual message loop implementation. Called by all flavors of Run().
+ // It will run the message loop in a SEH try block or not depending on the
+ // set_SEH_restoration() flag.
+ void RunInternal(Dispatcher* dispatcher);
+
+ //----------------------------------------------------------------------------
+ // A list of alternate message loop priority systems. The strategy_selector_
+ // determines which one to actually use.
+ void RunTraditional();
+
+ //----------------------------------------------------------------------------
+ // A list of method wrappers with identical calling signatures (no arguments)
+ // for use in the main message loop. Method pointers to these methods may be
+ // called round-robin from the main message loop, on any desired schedule.
+
+ bool ProcessNextDeferredTask();
+ bool ProcessNextDelayedNonNestableTask();
+ bool ProcessNextObject();
+ bool ProcessSomeTimers();
+
+ //----------------------------------------------------------------------------
+ // Process some pending messages.
+ // Returns true if a message was processed.
+ bool ProcessNextWindowsMessage();
+
+ // Wait until either an object is signaled, a message is available, a timer
+ // needs attention, or our incoming_queue_ has gotten a task.
+ // Handle (without returning) any APCs (only IO thread currently has APCs.)
+ void WaitForWork();
+
+ // Helper function for processing window messages. This includes handling
+ // WM_QUIT, message translation and dispatch, etc.
+ //
+ // If dispatcher_ is non-NULL this method does NOT dispatch the event, instead
+ // it invokes Dispatch on the dispatcher_.
+ bool ProcessMessageHelper(const MSG& msg);
+
+ // When we encounter a kMsgPumpATask, the following helper can be called to
+ // peek and process a replacement message, such as a WM_PAINT or WM_TIMER.
+ // The goal is to make the kMsgPumpATask as non-intrusive as possible, even
+ // though a continuous stream of such messages are posted. This method
+ // carefully peeks a message while there is no chance for a kMsgPumpATask to
+ // be pending, then releases the lock (allowing a replacement kMsgPumpATask to
+ // possibly be posted), and finally dispatches that peeked replacement.
+ // Note that the re-post of kMsgPumpATask may be asynchronous to this thread!!
+ bool ProcessPumpReplacementMessage();
+
+ // Signals a watcher if a wait falls within the range of objects we're
+ // waiting on. object_index is the offset in objects_ that was signaled.
+ // Returns true if an object was signaled.
+ bool SignalWatcher(size_t object_index);
+
+ // Run a work_queue_ task or new_task, and delete it (if it was processed by
+ // PostTask). If there are queued tasks, the oldest one is executed and
+ // new_task is queued. new_task is optional and can be NULL. In this NULL
+ // case, the method will run one pending task (if any exist). Returns true if
+ // it executes a task.
+ // Queued tasks accumulate only when there is a nonreentrant task currently
+ // processing, in which case the new_task is appended to the list
+ // work_queue_. Such re-entrancy generally happens when an unrequested
+ // message pump (typical of a native dialog) is executing in the context of a
+ // task.
+ bool QueueOrRunTask(Task* new_task);
+
+ // Runs the specified task and deletes it.
+ void RunTask(Task* task);
+
+ // Make state adjustments just before and after running tasks so that we can
+ // continue to work if a native message loop is employed during a task.
+ void BeforeTaskRunSetup();
+ void AfterTaskRunRestore();
+
+ // When processing messages in our MessageWndProc(), we are sometimes called
+ // by a native message pump (i.e., We are not called out of our Run() pump).
+ // In those cases, we need to process tasks during the Windows Message
+ // callback. This method processes a task, and also posts a new kMsgPumpATask
+ // messages to the Windows Msg Queue so that we are called back later (to
+ // process additional tasks).
+ void PumpATaskDuringWndProc();
+
+ // Load tasks from the incoming_queue_ into work_queue_ if the latter is
+ // empty. The former requires a lock to access, while the latter is directly
+ // accessible on this thread.
+ void ReloadWorkQueue();
+
+ // Delete tasks that haven't run yet without running them. Used in the
+ // destructor to make sure all the task's destructors get called.
+ void DeletePendingTasks();
+
+ // Make sure a kPumpATask message is in flight, which starts/continues the
+ // sub-pump.
+ void EnsurePumpATaskWasPosted();
+
+ // Do a PostMessage(), and crash if we can't eventually do the post.
+ void EnsureMessageGetsPosted(int message) const;
+
+ // Post a task to our incomming queue.
+ void MessageLoop::PostTaskInternal(Task* task);
+
+ // Start recording histogram info about events and action IF it was enabled
+ // and IF the statistics recorder can accept a registration of our histogram.
+ void StartHistogrammer();
+
+ // Add occurence of event to our histogram, so that we can see what is being
+ // done in a specific MessageLoop instance (i.e., specific thread).
+ // If message_histogram_ is NULL, this is a no-op.
+ void HistogramEvent(int event);
+
+ static TLSSlot tls_index_;
+ static int strategy_selector_;
+ static const LinearHistogram::DescriptionPair event_descriptions_[];
+ static bool enable_histogrammer_;
+
+ TimerManager timer_manager_;
+
+ // 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.
+ // As an optimization, when we don't need to use the prioritization of
+ // work_queue_, we use a null terminated list (TaskQueue) as our
+ // implementation of the queue. This saves on memory (list uses pointers
+ // internal to Task) and probably runs faster than the priority queue when
+ // there was no real prioritization.
+ OptionallyPrioritizedTaskQueue work_queue_;
+
+ // A vector of objects (and corresponding watchers) that are routinely
+ // serviced by this message loop's pump.
+ std::vector<HANDLE> objects_;
+ std::vector<Watcher*> watchers_;
+
+ ObserverList<Observer> observers_;
+ HWND message_hwnd_;
+ IDMap<Task> timed_tasks_;
+ // A recursion block that prevents accidentally running additonal tasks when
+ // insider a (accidentally induced?) nested message pump.
+ bool nestable_tasks_allowed_;
+
+ bool exception_restoration_;
+
+ Dispatcher* dispatcher_;
+ bool quit_received_;
+ bool quit_now_;
+
+ std::string thread_name_;
+ // A profiling histogram showing the counts of various messages and events.
+ scoped_ptr<LinearHistogram> message_histogram_;
+
+ // A null terminated list which creates an incoming_queue of tasks that are
+ // aquired under a mutex for processing on this instance's thread. These tasks
+ // have not yet been sorted out into items for our work_queue_ vs items that
+ // will be handled by the TimerManager.
+ TaskQueue incoming_queue_;
+ // Protect access to incoming_queue_.
+ Lock incoming_queue_lock_;
+
+ // A null terminated list of non-nestable tasks that we had to delay because
+ // when it came time to execute them we were in a nested message loop. They
+ // will execute once we're out of nested message loops.
+ TaskQueue delayed_non_nestable_queue_;
+
+ // Indicate if there is a kMsgPumpATask message pending in the Windows Message
+ // queue. There is at most one such message, and it can drive execution of
+ // tasks when a native message pump is running.
+ bool task_pump_message_pending_;
+ // Protect access to task_pump_message_pending_.
+ Lock task_pump_message_lock_;
+
+ // Used to count how many Run() invocations are on the stack.
+ int run_depth_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MessageLoop);
+};
+
+#endif // BASE_MESSAGE_LOOP_H__
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
new file mode 100644
index 0000000..374a618
--- /dev/null
+++ b/base/message_loop_unittest.cc
@@ -0,0 +1,827 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/scoped_handle.h"
+#include "base/thread.h"
+#include "base/ref_counted.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class MessageLoopTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ enable_recursive_task_ = MessageLoop::current()->NestableTasksAllowed();
+ }
+ virtual void TearDown() {
+ MessageLoop::current()->SetNestableTasksAllowed(enable_recursive_task_);
+ }
+ private:
+ bool enable_recursive_task_;
+};
+
+class Foo : public base::RefCounted<Foo> {
+ public:
+ Foo() : test_count_(0) {
+ }
+
+ void Test0() {
+ ++test_count_;
+ }
+
+ void Test1ConstRef(const std::string& a) {
+ ++test_count_;
+ result_.append(a);
+ }
+
+ void Test1Ptr(std::string* a) {
+ ++test_count_;
+ result_.append(*a);
+ }
+
+ void Test1Int(int a) {
+ test_count_ += a;
+ }
+
+ void Test2Ptr(std::string* a, std::string* b) {
+ ++test_count_;
+ result_.append(*a);
+ result_.append(*b);
+ }
+
+ void Test2Mixed(const std::string& a, std::string* b) {
+ ++test_count_;
+ result_.append(a);
+ result_.append(*b);
+ }
+
+ int test_count() const { return test_count_; }
+ const std::string& result() const { return result_; }
+
+ private:
+ int test_count_;
+ std::string result_;
+};
+
+class QuitMsgLoop : public base::RefCounted<QuitMsgLoop> {
+ public:
+ void QuitNow() {
+ MessageLoop::current()->Quit();
+ }
+};
+
+} // namespace
+
+TEST(MessageLoopTest, PostTask) {
+ // 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, NewRunnableMethod(
+ foo.get(), &Foo::Test0));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1ConstRef, a));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1Ptr, &b));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1Int, 100));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test2Ptr, &a, &c));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test2Mixed, a, &d));
+
+ // After all tests, post a message that will shut down the message loop
+ scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ quit.get(), &QuitMsgLoop::QuitNow));
+
+ // Now kick things off
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(foo->test_count(), 105);
+ EXPECT_EQ(foo->result(), "abacad");
+}
+
+TEST(MessageLoopTest, InvokeLater_SEH) {
+ // 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, NewRunnableMethod(
+ foo.get(), &Foo::Test0));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1ConstRef, a));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1Ptr, &b));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test1Int, 100));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test2Ptr, &a, &c));
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ foo.get(), &Foo::Test2Mixed, a, &d));
+
+ // After all tests, post a message that will shut down the message loop
+ scoped_refptr<QuitMsgLoop> quit = new QuitMsgLoop();
+ MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
+ quit.get(), &QuitMsgLoop::QuitNow));
+
+ // 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");
+}
+
+namespace {
+
+class NestingTest : public Task {
+ public:
+ explicit NestingTest(int* depth) : depth_(depth) {
+ }
+ void Run() {
+ if (*depth_ > 0) {
+ *depth_ -= 1;
+ MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(depth_));
+
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ }
+ MessageLoop::current()->Quit();
+ }
+ private:
+ int* depth_;
+};
+
+LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) {
+ ADD_FAILURE() << "bad exception handler";
+ ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode);
+ return EXCEPTION_EXECUTE_HANDLER;
+}
+
+// This task throws an SEH exception: initially write to an invalid address.
+// If the right SEH filter is installed, it will fix the error.
+class CrasherTask : public Task {
+ public:
+ // Ctor. If trash_SEH_handler is true, the task will override the unhandled
+ // exception handler with one sure to crash this test.
+ explicit CrasherTask(bool trash_SEH_handler)
+ : trash_SEH_handler_(trash_SEH_handler) {
+ }
+ void Run() {
+ Sleep(1);
+ if (trash_SEH_handler_)
+ ::SetUnhandledExceptionFilter(&BadExceptionHandler);
+ // Generate a SEH fault. We do it in asm to make sure we know how to undo
+ // the damage.
+ __asm {
+ mov eax, dword ptr [CrasherTask::bad_array_]
+ mov byte ptr [eax], 66
+ }
+ MessageLoop::current()->Quit();
+ }
+ // Points the bad array to a valid memory location.
+ static void FixError() {
+ bad_array_ = &valid_store_;
+ }
+
+ private:
+ bool trash_SEH_handler_;
+ static volatile char* bad_array_;
+ static char valid_store_;
+};
+
+volatile char* CrasherTask::bad_array_ = 0;
+char CrasherTask::valid_store_ = 0;
+
+// This SEH filter fixes the problem and retries execution. Fixing requires
+// that the last instruction: mov eax, [CrasherTask::bad_array_] to be retried
+// so we move the instruction pointer 5 bytes back.
+LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) {
+ if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION)
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ CrasherTask::FixError();
+ ex_info->ContextRecord->Eip -= 5;
+ return EXCEPTION_CONTINUE_EXECUTION;
+}
+
+} // namespace
+
+
+TEST(MessageLoopTest, Crasher) {
+ if (::IsDebuggerPresent())
+ return;
+
+ LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+ ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
+
+ MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(false));
+ MessageLoop::current()->set_exception_restoration(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+
+TEST(MessageLoopTest, CrasherNasty) {
+ if (::IsDebuggerPresent())
+ return;
+
+ LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter =
+ ::SetUnhandledExceptionFilter(&HandleCrasherTaskException);
+
+ MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(true));
+ MessageLoop::current()->set_exception_restoration(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->set_exception_restoration(false);
+
+ ::SetUnhandledExceptionFilter(old_SEH_filter);
+}
+
+TEST(MessageLoopTest, Nesting) {
+ int depth = 100;
+ MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth));
+ MessageLoop::current()->Run();
+ EXPECT_EQ(depth, 0);
+}
+
+namespace {
+
+const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test";
+
+enum TaskType {
+ MESSAGEBOX,
+ ENDDIALOG,
+ RECURSIVE,
+ TIMEDMESSAGELOOP,
+ QUITMESSAGELOOP,
+ ORDERERD,
+ PUMPS,
+};
+
+// Saves the order in which the tasks executed.
+struct TaskItem {
+ TaskItem(TaskType t, int c, bool s)
+ : type(t),
+ cookie(c),
+ start(s) {
+ }
+
+ TaskType type;
+ int cookie;
+ bool start;
+
+ bool operator == (const TaskItem& other) const {
+ return type == other.type && cookie == other.cookie && start == other.start;
+ }
+};
+
+typedef std::vector<TaskItem> TaskList;
+
+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 ORDERERD: os << "ORDERERD"; break;
+ case PUMPS: os << "PUMPS"; 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";
+}
+
+// Saves the order the tasks ran.
+class OrderedTasks : public Task {
+ public:
+ OrderedTasks(TaskList* order, int cookie)
+ : order_(order),
+ type_(ORDERERD),
+ cookie_(cookie) {
+ }
+ OrderedTasks(TaskList* order, TaskType type, int cookie)
+ : order_(order),
+ type_(type),
+ cookie_(cookie) {
+ }
+
+ void RunStart() {
+ TaskItem item(type_, cookie_, true);
+ DLOG(INFO) << item;
+ order_->push_back(item);
+ }
+ void RunEnd() {
+ TaskItem item(type_, cookie_, false);
+ DLOG(INFO) << item;
+ order_->push_back(item);
+ }
+
+ virtual void Run() {
+ RunStart();
+ RunEnd();
+ }
+
+ protected:
+ TaskList* order() const {
+ return order_;
+ }
+
+ int cookie() const {
+ return cookie_;
+ }
+
+ private:
+ TaskList* order_;
+ TaskType type_;
+ int cookie_;
+};
+
+// MessageLoop implicitly start a "modal message loop". Modal dialog boxes,
+// common controls (like OpenFile) and StartDoc printing function can cause
+// implicit message loops.
+class MessageBoxTask : public OrderedTasks {
+ public:
+ MessageBoxTask(TaskList* order, int cookie, bool is_reentrant)
+ : OrderedTasks(order, MESSAGEBOX, cookie),
+ is_reentrant_(is_reentrant) {
+ }
+
+ virtual void Run() {
+ RunStart();
+ if (is_reentrant_)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK);
+ RunEnd();
+ }
+
+ private:
+ bool is_reentrant_;
+};
+
+// Will end the MessageBox.
+class EndDialogTask : public OrderedTasks {
+ public:
+ EndDialogTask(TaskList* order, int cookie)
+ : OrderedTasks(order, ENDDIALOG, cookie) {
+ }
+
+ virtual void Run() {
+ RunStart();
+ HWND window = GetActiveWindow();
+ if (window != NULL) {
+ EXPECT_NE(EndDialog(window, IDCONTINUE), 0);
+ // Cheap way to signal that the window wasn't found if RunEnd() isn't
+ // called.
+ RunEnd();
+ }
+ }
+};
+
+class RecursiveTask : public OrderedTasks {
+ public:
+ RecursiveTask(int depth, TaskList* order, int cookie, bool is_reentrant)
+ : OrderedTasks(order, RECURSIVE, cookie),
+ depth_(depth),
+ is_reentrant_(is_reentrant) {
+ }
+
+ virtual void Run() {
+ RunStart();
+ if (depth_ > 0) {
+ if (is_reentrant_)
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new RecursiveTask(depth_ - 1, order(), cookie(), is_reentrant_));
+ }
+ RunEnd();
+ }
+
+ private:
+ int depth_;
+ bool is_reentrant_;
+};
+
+class QuitTask : public OrderedTasks {
+ public:
+ QuitTask(TaskList* order, int cookie)
+ : OrderedTasks(order, QUITMESSAGELOOP, cookie) {
+ }
+
+ virtual void Run() {
+ RunStart();
+ MessageLoop::current()->Quit();
+ RunEnd();
+ }
+};
+
+class Recursive2Tasks : public Task {
+ public:
+ Recursive2Tasks(MessageLoop* target,
+ HANDLE event,
+ bool expect_window,
+ TaskList* order,
+ bool is_reentrant)
+ : target_(target),
+ event_(event),
+ expect_window_(expect_window),
+ order_(order),
+ is_reentrant_(is_reentrant) {
+ }
+
+ virtual void Run() {
+ target_->PostTask(FROM_HERE,
+ new RecursiveTask(2, order_, 1, is_reentrant_));
+ target_->PostTask(FROM_HERE,
+ new MessageBoxTask(order_, 2, is_reentrant_));
+ target_->PostTask(FROM_HERE,
+ new RecursiveTask(2, order_, 3, is_reentrant_));
+ // The trick here is that for recursive task processing, this task will be
+ // ran _inside_ the MessageBox message loop, dismissing the MessageBox
+ // without a chance.
+ // For non-recursive task processing, this will be executed _after_ the
+ // MessageBox will have been dismissed by the code below, where
+ // expect_window_ is true.
+ target_->PostTask(FROM_HERE, new EndDialogTask(order_, 4));
+ target_->PostTask(FROM_HERE, new QuitTask(order_, 5));
+
+ // Enforce that every tasks are sent before starting to run the main thread
+ // message loop.
+ ASSERT_TRUE(SetEvent(event_));
+
+ // Poll for the MessageBox. Don't do this at home! At the speed we do it,
+ // you will never realize one MessageBox was shown.
+ for (; expect_window_;) {
+ HWND window = FindWindow(L"#32770", kMessageBoxTitle);
+ if (window) {
+ // Dismiss it.
+ for (;;) {
+ HWND button = FindWindowEx(window, NULL, L"Button", NULL);
+ if (button != NULL) {
+ EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONDOWN, 0, 0));
+ EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONUP, 0, 0));
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ private:
+ MessageLoop* target_;
+ HANDLE event_;
+ TaskList* order_;
+ bool expect_window_;
+ bool is_reentrant_;
+};
+
+} // namespace
+
+TEST(MessageLoop, RecursiveDenial1) {
+ EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed());
+ TaskList order;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new RecursiveTask(2, &order, 1, false));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new RecursiveTask(2, &order, 2, false));
+ MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(order.size(), 14);
+ EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
+}
+
+
+TEST(MessageLoop, RecursiveSupport1) {
+ TaskList order;
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new RecursiveTask(2, &order, 1, true));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new RecursiveTask(2, &order, 2, true));
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new QuitTask(&order, 3));
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(order.size(), 14);
+ EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+ EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false));
+ EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true));
+ EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false));
+}
+
+// A side effect of this test is the generation a beep. Sorry.
+TEST(MessageLoop, RecursiveDenial2) {
+ Thread worker("RecursiveDenial2_worker");
+ ASSERT_EQ(true, worker.Start());
+ TaskList order;
+ ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+ worker.message_loop()->PostTask(FROM_HERE,
+ new Recursive2Tasks(MessageLoop::current(),
+ event,
+ true,
+ &order,
+ false));
+ // Let the other thread execute.
+ WaitForSingleObject(event, INFINITE);
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(order.size(), 17);
+ EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
+ EXPECT_EQ(order[ 3], TaskItem(MESSAGEBOX, 2, false));
+ EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 3, false));
+ // When EndDialogTask is processed, the window is already dismissed, hence no
+ // "end" entry.
+ EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, true));
+ EXPECT_EQ(order[ 7], TaskItem(QUITMESSAGELOOP, 5, true));
+ EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, false));
+ EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[11], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[15], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false));
+}
+
+// A side effect of this test is the generation a beep. Sorry.
+TEST(MessageLoop, RecursiveSupport2) {
+ Thread worker("RecursiveSupport2_worker");
+ ASSERT_EQ(true, worker.Start());
+ TaskList order;
+ ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL));
+ worker.message_loop()->PostTask(FROM_HERE,
+ new Recursive2Tasks(MessageLoop::current(),
+ event,
+ false,
+ &order,
+ true));
+ // Let the other thread execute.
+ WaitForSingleObject(event, INFINITE);
+ MessageLoop::current()->Run();
+
+ ASSERT_EQ(order.size(), 18);
+ EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true));
+ // Note that this executes in the MessageBox modal loop.
+ EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order[ 5], TaskItem(ENDDIALOG, 4, true));
+ EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, false));
+ EXPECT_EQ(order[ 7], TaskItem(MESSAGEBOX, 2, false));
+ /* The order can subtly change here. The reason is that when RecursiveTask(1)
+ is called in the main thread, if it is faster than getting to the
+ PostTask(FROM_HERE, QuitTask) execution, the order of task execution can
+ change. We don't care anyway that the order isn't correct.
+ EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
+ EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
+ EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false));
+ */
+ EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[13], TaskItem(RECURSIVE, 3, false));
+ EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, true));
+ EXPECT_EQ(order[15], TaskItem(RECURSIVE, 1, false));
+ EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, true));
+ EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, false));
+}
+
+class TaskThatPumps : public OrderedTasks {
+ public:
+ TaskThatPumps(TaskList* order, int cookie)
+ : OrderedTasks(order, PUMPS, cookie) {
+ }
+
+ virtual void Run() {
+ RunStart();
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->Quit();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+ RunEnd();
+ }
+
+ private:
+};
+
+
+// Tests that non nestable tasks run in FIFO if there are no nested loops.
+TEST(MessageLoop, NonNestableWithNoNesting) {
+ TaskList order;
+
+ Task* task = new OrderedTasks(&order, 1);
+ task->set_nestable(false);
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2));
+ MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3));
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(order.size(), 6);
+ EXPECT_EQ(order[ 0], TaskItem(ORDERERD, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 1, false));
+ EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 2, true));
+ EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 2, false));
+ EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true));
+ EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false));
+}
+
+// Tests that non nestable tasks don't run when there's code in the call stack.
+TEST(MessageLoop, NonNestableInNestedLoop) {
+ TaskList order;
+
+ MessageLoop::current()->PostTask(FROM_HERE,
+ new TaskThatPumps(&order, 1));
+ Task* task = new OrderedTasks(&order, 2);
+ task->set_nestable(false);
+ MessageLoop::current()->PostTask(FROM_HERE, task);
+ MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3));
+ MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 4));
+ Task* non_nestable_quit = new QuitTask(&order, 5);
+ non_nestable_quit->set_nestable(false);
+ MessageLoop::current()->PostTask(FROM_HERE, non_nestable_quit);
+
+
+ MessageLoop::current()->Run();
+
+ // FIFO order.
+ ASSERT_EQ(order.size(), 10);
+ EXPECT_EQ(order[ 0], TaskItem(PUMPS, 1, true));
+ EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 3, true));
+ EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 3, false));
+ EXPECT_EQ(order[ 3], TaskItem(QUITMESSAGELOOP, 4, true));
+ EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 4, false));
+ EXPECT_EQ(order[ 5], TaskItem(PUMPS, 1, false));
+ EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 2, true));
+ EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 2, false));
+ EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true));
+ EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false));
+}
+
+
+namespace {
+
+class AutoresetWatcher : public MessageLoop::Watcher {
+ public:
+ AutoresetWatcher(HANDLE signal, MessageLoop* message_loop)
+ : signal_(signal), message_loop_(message_loop) {}
+ virtual void OnObjectSignaled(HANDLE object);
+ private:
+ HANDLE signal_;
+ MessageLoop* message_loop_;
+};
+
+void AutoresetWatcher::OnObjectSignaled(HANDLE object) {
+ message_loop_->WatchObject(object, NULL);
+ ASSERT_TRUE(SetEvent(signal_));
+}
+
+class AutoresetTask : public Task {
+ public:
+ AutoresetTask(HANDLE object, MessageLoop::Watcher* watcher)
+ : object_(object), watcher_(watcher) {}
+ virtual void Run() {
+ MessageLoop::current()->WatchObject(object_, watcher_);
+ }
+
+ private:
+ HANDLE object_;
+ MessageLoop::Watcher* watcher_;
+};
+
+} // namespace
+
+TEST(MessageLoop, AutoresetEvents) {
+ SECURITY_ATTRIBUTES attributes;
+ attributes.nLength = sizeof(attributes);
+ attributes.bInheritHandle = false;
+ attributes.lpSecurityDescriptor = NULL;
+
+ // Init an autoreset and a manual reset events.
+ HANDLE autoreset = CreateEvent(&attributes, FALSE, FALSE, NULL);
+ HANDLE callback_called = CreateEvent(&attributes, TRUE, FALSE, NULL);
+ ASSERT_TRUE(NULL != autoreset);
+ ASSERT_TRUE(NULL != callback_called);
+
+ Thread thread("Autoreset test");
+ ASSERT_TRUE(thread.Start());
+
+ MessageLoop* message_loop = thread.message_loop();
+ ASSERT_TRUE(NULL != message_loop);
+
+ AutoresetWatcher watcher(callback_called, message_loop);
+ AutoresetTask* task = new AutoresetTask(autoreset, &watcher);
+ message_loop->PostTask(FROM_HERE, task);
+ Sleep(100); // Make sure the thread runs and sleeps for lack of work.
+
+ ASSERT_TRUE(SetEvent(autoreset));
+
+ DWORD result = WaitForSingleObject(callback_called, 1000);
+ EXPECT_EQ(WAIT_OBJECT_0, result);
+
+ thread.Stop();
+}
+
+namespace {
+
+class DispatcherImpl : public MessageLoop::Dispatcher {
+ public:
+ DispatcherImpl() : dispatch_count_(0) {}
+
+ virtual bool Dispatch(const MSG& msg) {
+ ::TranslateMessage(&msg);
+ ::DispatchMessage(&msg);
+ return (++dispatch_count_ != 2);
+ }
+
+ int dispatch_count_;
+};
+
+} // namespace
+
+TEST(MessageLoop, Dispatcher) {
+ class MyTask : public Task {
+ public:
+ virtual void Run() {
+ PostMessage(NULL, WM_LBUTTONDOWN, 0, 0);
+ PostMessage(NULL, WM_LBUTTONUP, 'A', 0);
+ }
+ };
+ Task* task = new MyTask();
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100);
+ DispatcherImpl dispatcher;
+ MessageLoop::current()->Run(&dispatcher);
+ ASSERT_EQ(2, dispatcher.dispatch_count_);
+}
diff --git a/base/multiprocess_test.h b/base/multiprocess_test.h
new file mode 100644
index 0000000..21127f3
--- /dev/null
+++ b/base/multiprocess_test.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_MULTIPROCESS_TEST_H__
+#define BASE_MULTIPROCESS_TEST_H__
+
+#include "base/command_line.h"
+#include "base/process_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Command line switch to invoke a child process rather than
+// to run the normal test suite.
+static const wchar_t kRunClientProcess[] = L"client";
+
+// A MultiProcessTest is a test class which makes it easier to
+// write a test which requires code running out of process.
+//
+// To create a multiprocess test simply follow these steps:
+//
+// 1) Derive your test from MultiProcessTest.
+// 2) Modify your mainline so that if it sees the
+// kRuNClientProcess switch, it will deal with it.
+// 3) Create a mainline function for the child processes
+// 4) Call SpawnChild("foo"), where "foo" is the name of
+// the function you wish to run in the child processes.
+// That's it!
+//
+class MultiProcessTest : public testing::Test {
+ public:
+ // Prototype function for a client function. Multi-process
+ // clients must provide a callback with this signature to run.
+ typedef int (__cdecl *ChildFunctionPtr)();
+
+ protected:
+ // Run a child process.
+ // 'procname' is the name of a function which the child will
+ // execute. It must be exported from this library in order to
+ // run.
+ //
+ // Example signature:
+ // extern "C" int __declspec(dllexport) FooBar() {
+ // // do client work here
+ // }
+ //
+ // Returns the handle to the child, or NULL on failure
+ HANDLE SpawnChild(const std::wstring& procname) {
+ std::wstring cl(GetCommandLineW());
+ CommandLine::AppendSwitchWithValue(&cl, kRunClientProcess, procname);
+ // TODO(darin): re-enable this once we have base/debug_util.h
+ //ProcessDebugFlags(&cl, DebugUtil::UNKNOWN, false);
+ HANDLE handle = NULL;
+ process_util::LaunchApp(cl, false, true, &handle);
+ return handle;
+ }
+};
+
+#endif // BASE_MULTIPROCESS_TEST_H__
diff --git a/base/no_windows2000_unittest.h b/base/no_windows2000_unittest.h
new file mode 100644
index 0000000..5bef8b8
--- /dev/null
+++ b/base/no_windows2000_unittest.h
@@ -0,0 +1,46 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_NO_WINDOWS2000_UNITTEST_H__
+#define BASE_NO_WINDOWS2000_UNITTEST_H__
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/win_util.h"
+
+// Disable the whole test case when executing on Windows 2000 or lower.
+// Note: Parent should be testing::Test or UITest.
+template<typename Parent>
+class NoWindows2000Test : public Parent {
+ public:
+ static bool IsTestCaseDisabled() {
+ return win_util::GetWinVersion() <= win_util::WINVERSION_2000;
+ }
+};
+
+#endif // BASE_NO_WINDOWS2000_UNITTEST_H__op
diff --git a/base/non_thread_safe.cc b/base/non_thread_safe.cc
new file mode 100644
index 0000000..eefc9a5
--- /dev/null
+++ b/base/non_thread_safe.cc
@@ -0,0 +1,48 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/non_thread_safe.h"
+
+#include "base/message_loop.h"
+
+// These checks are only done in release builds.
+#ifndef NDEBUG
+
+NonThreadSafe::NonThreadSafe() : valid_thread_id_(GetCurrentThreadId()) {
+}
+
+bool NonThreadSafe::CalledOnValidThread() const {
+ return valid_thread_id_ == GetCurrentThreadId();
+}
+
+NonThreadSafe::~NonThreadSafe() {
+ DCHECK(CalledOnValidThread());
+}
+
+#endif
diff --git a/base/non_thread_safe.h b/base/non_thread_safe.h
new file mode 100644
index 0000000..e3f4e97
--- /dev/null
+++ b/base/non_thread_safe.h
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_NON_THREAD_SAFE_H__
+#define BASE_NON_THREAD_SAFE_H__
+
+#include "base/logging.h"
+
+// A helper class used to help verify that methods of a class are
+// called from the same thread. One can inherit from this class and use
+// CalledOnValidThread() to verify.
+//
+// This is intended to be used with classes that appear to be thread safe, but
+// aren't. For example, a service or a singleton like the preferences system.
+//
+// Example:
+// class MyClass : public NonThreadSafe {
+// public:
+// void Foo() {
+// DCHECK(CalledOnValidThread());
+// ... (do stuff) ...
+// }
+// }
+//
+// In Release mode, CalledOnValidThread will always return true.
+//
+#ifndef NDEBUG
+class NonThreadSafe {
+ public:
+ NonThreadSafe();
+ ~NonThreadSafe();
+
+ protected:
+ bool CalledOnValidThread() const;
+
+ private:
+ int32 valid_thread_id_;
+};
+#else
+// Do nothing in release mode.
+class NonThreadSafe {
+ public:
+ NonThreadSafe() {}
+ ~NonThreadSafe() {}
+
+ protected:
+ bool CalledOnValidThread() const {
+ return true;
+ }
+};
+#endif // NDEBUG
+
+#endif // BASE_NON_THREAD_SAFE_H__
diff --git a/base/observer_list.h b/base/observer_list.h
new file mode 100644
index 0000000..a64808d
--- /dev/null
+++ b/base/observer_list.h
@@ -0,0 +1,170 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_OBSERVER_LIST_H__
+#define BASE_OBSERVER_LIST_H__
+
+#include <vector>
+#include <algorithm>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// OVERVIEW:
+//
+// A container for a list of observers. Unlike a normal STL vector or list,
+// this container can be modified during iteration without invalidating the
+// iterator. So, it safely handles the case of an observer removing itself
+// or other observers from the list while observers are being notified.
+//
+// TYPICAL USAGE:
+//
+// class MyWidget {
+// public:
+// ...
+//
+// class Observer {
+// public:
+// virtual void OnFoo(MyWidget* w) = 0;
+// virtual void OnBar(MyWidget* w, int x, int y) = 0;
+// };
+//
+// void AddObserver(Observer* obs) {
+// observer_list_.AddObserver(obs);
+// }
+//
+// void RemoveObserver(Observer* obs) {
+// observer_list_.RemoveObserver(obs);
+// }
+//
+// void NotifyFoo() {
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this));
+// }
+//
+// void NotifyBar(int x, int y) {
+// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y));
+// }
+//
+// private:
+// ObserverList<Observer> observer_list_;
+// };
+//
+///////////////////////////////////////////////////////////////////////////////
+
+template <class ObserverType, bool check_empty = false>
+class ObserverList {
+ public:
+ ObserverList() : notify_depth_(0) {}
+ ~ObserverList() {
+ // When check_empty is true, assert that the list is empty on destruction.
+ if (check_empty) {
+ Compact();
+ DCHECK_EQ(observers_.size(), 0);
+ }
+ }
+
+ // Add an observer to the list.
+ void AddObserver(ObserverType* obs) {
+ DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end())
+ << "Observers can only be added once!";
+ observers_.push_back(obs);
+ }
+
+ // Remove an observer from the list.
+ void RemoveObserver(ObserverType* obs) {
+ ListType::iterator it = find(observers_.begin(), observers_.end(), obs);
+ if (it != observers_.end()) {
+ if (notify_depth_) {
+ *it = 0;
+ } else {
+ observers_.erase(it);
+ }
+ }
+ }
+
+ // An iterator class that can be used to access the list of observers. See
+ // also the FOREACH_OBSERVER macro defined below.
+ class Iterator {
+ public:
+ Iterator(const ObserverList<ObserverType>& list) : list_(list), index_(0) {
+ ++list_.notify_depth_;
+ }
+
+ ~Iterator() {
+ if (--list_.notify_depth_ == 0)
+ list_.Compact();
+ }
+
+ ObserverType* GetNext() {
+ ListType& observers = list_.observers_;
+ // Advance if the current element is null
+ while (index_ < observers.size() && !observers[index_])
+ ++index_;
+ return index_ < observers.size() ? observers[index_++] : NULL;
+ }
+
+ private:
+ const ObserverList<ObserverType>& list_;
+ size_t index_;
+ };
+
+ private:
+ typedef std::vector<ObserverType*> ListType;
+
+ void Compact() const {
+ ListType::iterator it = observers_.begin();
+ while (it != observers_.end()) {
+ if (*it) {
+ ++it;
+ } else {
+ it = observers_.erase(it);
+ }
+ }
+ }
+
+ // These are marked mutable to facilitate having NotifyAll be const.
+ mutable ListType observers_;
+ mutable int notify_depth_;
+
+ friend class ObserverList::Iterator;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ObserverList);
+};
+
+#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \
+ do { \
+ ObserverList<ObserverType>::Iterator it(observer_list); \
+ ObserverType* obs; \
+ while (obs = it.GetNext()) \
+ obs->func; \
+ } while (0)
+
+#endif // BASE_OBSERVER_LIST_H__
diff --git a/base/observer_list_unittest.cc b/base/observer_list_unittest.cc
new file mode 100644
index 0000000..6bb1d63
--- /dev/null
+++ b/base/observer_list_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/observer_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class ObserverListTest : public testing::Test {
+};
+
+class Foo {
+ public:
+ virtual void Observe(int x) = 0;
+};
+
+class Adder : public Foo {
+ public:
+ Adder(int scaler) : scaler_(scaler), total(0) {}
+ virtual void Observe(int x) {
+ total += x * scaler_;
+ }
+ int total;
+ private:
+ int scaler_;
+};
+
+class Disrupter : public Foo {
+ public:
+ Disrupter(ObserverList<Foo>& list, Foo* doomed) : list_(list), doomed_(doomed) {
+ }
+ virtual void Observe(int x) {
+ list_.RemoveObserver(doomed_);
+ }
+ private:
+ ObserverList<Foo>& list_;
+ Foo* doomed_;
+};
+
+} // namespace
+
+TEST(ObserverListTest, BasicTest) {
+ ObserverList<Foo> observer_list;
+ Adder a(1), b(-1), c(1), d(-1);
+ Disrupter evil(observer_list, &c);
+
+ observer_list.AddObserver(&a);
+ observer_list.AddObserver(&b);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+ observer_list.AddObserver(&evil);
+ observer_list.AddObserver(&c);
+ observer_list.AddObserver(&d);
+
+ FOR_EACH_OBSERVER(Foo, observer_list, Observe(10));
+
+ EXPECT_EQ(a.total, 20);
+ EXPECT_EQ(b.total, -20);
+ EXPECT_EQ(c.total, 0);
+ EXPECT_EQ(d.total, -10);
+}
diff --git a/base/path_service.cc b/base/path_service.cc
new file mode 100644
index 0000000..f4b3a83
--- /dev/null
+++ b/base/path_service.cc
@@ -0,0 +1,202 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+
+#include <hash_map>
+#include <hash_set>
+
+#include "base/path_service.h"
+
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/file_util.h"
+
+namespace base {
+ bool PathProvider(int key, std::wstring* result);
+}
+
+namespace {
+
+typedef stdext::hash_map<int,std::wstring> PathMap;
+typedef stdext::hash_set<int> PathSet;
+
+// We keep a linked list of providers. In a debug build we ensure that no two
+// providers claim overlapping keys.
+struct Provider {
+ PathService::ProviderFunc func;
+ struct Provider* next;
+#ifndef NDEBUG
+ int key_start;
+ int key_end;
+#endif
+};
+
+static Provider base_provider = {
+ base::PathProvider,
+ NULL,
+#ifndef NDEBUG
+ base::PATH_START,
+ base::PATH_END
+#endif
+};
+
+struct PathData {
+ Lock lock;
+ PathMap cache; // Track mappings from path key to path value.
+ PathSet overrides; // Track whether a path has been overridden.
+ Provider* providers; // Linked list of path service providers.
+
+ PathData() : providers(&base_provider) {
+ }
+};
+
+// We rely on the path service not being used prior to 'main' execution, and
+// we are happy to let this data structure leak at process exit.
+PathData* path_data = new PathData();
+
+} // namespace
+
+// TODO(brettw): this function does not handle long paths (filename > MAX_PATH)
+// characters). This isn't supported very well by Windows right now, so it is
+// moot, but we should keep this in mind for the future.
+// static
+bool PathService::Get(int key, std::wstring* result) {
+ DCHECK(path_data);
+ DCHECK(result);
+ DCHECK(key >= base::DIR_CURRENT);
+
+ // special case the current directory because it can never be cached
+ if (key == base::DIR_CURRENT) {
+ wchar_t system_buffer[MAX_PATH];
+ system_buffer[0] = 0;
+ DWORD len = GetCurrentDirectory(MAX_PATH, system_buffer);
+ if (len == 0 || len > MAX_PATH)
+ return false;
+ *result = system_buffer;
+ file_util::TrimTrailingSeparator(result);
+ return true;
+ }
+
+ // TODO(darin): it would be nice to avoid holding this lock while calling out
+ // to the path providers.
+ AutoLock scoped_lock(path_data->lock);
+
+ // check for a cached version
+ PathMap::const_iterator it = path_data->cache.find(key);
+ if (it != path_data->cache.end()) {
+ *result = it->second;
+ return true;
+ }
+
+ std::wstring path;
+
+ // search providers for the requested path
+ Provider* provider = path_data->providers;
+ while (provider) {
+ if (provider->func(key, &path))
+ break;
+ DCHECK(path.empty()) << "provider should not have modified path";
+ provider = provider->next;
+ }
+
+ if (path.empty())
+ return false;
+
+ // Save the computed path in our cache.
+ path_data->cache[key] = path;
+
+ result->swap(path);
+ return true;
+}
+
+bool PathService::IsOverridden(int key) {
+ DCHECK(path_data);
+
+ AutoLock scoped_lock(path_data->lock);
+ return path_data->overrides.find(key) != path_data->overrides.end();
+}
+
+bool PathService::Override(int key, const std::wstring& path) {
+ DCHECK(path_data);
+ DCHECK(key > base::DIR_CURRENT) << "invalid path key";
+
+ wchar_t file_path_buf[MAX_PATH];
+ if (!_wfullpath(file_path_buf, path.c_str(), MAX_PATH))
+ return false;
+ std::wstring file_path(file_path_buf);
+
+ // make sure the directory exists:
+ if (!file_util::PathExists(file_path) &&
+ // TODO(darin): what if this path is not that of a directory?
+ !file_util::CreateDirectory(file_path))
+ return false;
+
+ file_util::TrimTrailingSeparator(&file_path);
+
+ AutoLock scoped_lock(path_data->lock);
+ path_data->cache[key] = file_path;
+ path_data->overrides.insert(key);
+ return true;
+}
+
+bool PathService::SetCurrentDirectory(const std::wstring& current_directory) {
+ BOOL ret = ::SetCurrentDirectory(current_directory.c_str());
+ return (ret ? true : false);
+}
+
+void PathService::RegisterProvider(ProviderFunc func, int key_start,
+ int key_end) {
+ DCHECK(path_data);
+ DCHECK(key_end > key_start);
+
+ AutoLock scoped_lock(path_data->lock);
+
+ Provider* p;
+
+#ifndef NDEBUG
+ p = path_data->providers;
+ while (p) {
+ DCHECK(key_start >= p->key_end || key_end <= p->key_start) <<
+ "path provider collision";
+ p = p->next;
+ }
+#endif
+
+ p = new Provider;
+ p->func = func;
+ p->next = path_data->providers;
+#ifndef NDEBUG
+ p->key_start = key_start;
+ p->key_end = key_end;
+#endif
+ path_data->providers = p;
+}
diff --git a/base/path_service.h b/base/path_service.h
new file mode 100644
index 0000000..861c9b5
--- /dev/null
+++ b/base/path_service.h
@@ -0,0 +1,85 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PATH_SERVICE_H__
+#define BASE_PATH_SERVICE_H__
+
+#include <string>
+
+#include "base/base_paths.h"
+
+// The path service is a global table mapping keys to file system paths. It is
+// OK to use this service from multiple threads.
+//
+class PathService {
+ public:
+ // Retrieves a path to a special directory or file and places it into the
+ // string pointed to by 'path'. If you ask for a directory it is guaranteed
+ // to NOT have a path separator at the end. For example, "c:\windows\temp"
+ // Directories are also guaranteed to exist when this function succeeds.
+ //
+ // Returns true if the directory or file was successfully retrieved. On
+ // failure, 'path' will not be changed.
+ static bool Get(int key, std::wstring* path);
+
+ // Overrides the path to a special directory or file. This cannot be used to
+ // change the value of DIR_CURRENT, but that should be obvious. Also, if the
+ // path specifies a directory that does not exist, the directory will be
+ // created by this method. This method returns true if successful.
+ //
+ // If the given path is relative, then it will be resolved against DIR_CURRENT.
+ //
+ // WARNING: Consumers of PathService::Get may expect paths to be constant
+ // over the lifetime of the app, so this method should be used with caution.
+ static bool Override(int key, const std::wstring& path);
+
+ // Return whether a path was overridden.
+ static bool IsOverridden(int key);
+
+ // Sets the current directory.
+ static bool SetCurrentDirectory(const std::wstring& current_directory);
+
+ // To extend the set of supported keys, you can register a path provider,
+ // which is just a function mirroring PathService::Get. The ProviderFunc
+ // returns false if it cannot provide a non-empty path for the given key.
+ // Otherwise, true is returned.
+ //
+ // WARNING: This function could be called on any thread from which the
+ // PathService is used, so a the ProviderFunc MUST BE THREADSAFE.
+ //
+ typedef bool (*ProviderFunc)(int, std::wstring*);
+
+ // Call to register a path provider. You must specify the range "[key_start,
+ // key_end)" of supported path keys.
+ static void RegisterProvider(ProviderFunc provider,
+ int key_start,
+ int key_end);
+};
+
+#endif // BASE_PATH_SERVICE_H__
diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc
new file mode 100644
index 0000000..8840650
--- /dev/null
+++ b/base/path_service_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/path_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class PathServiceTest : public testing::Test {
+ };
+};
+
+// Returns true if PathService::Get returns true and sets the path parameter
+// to non-empty for the given PathService::DirType enumeration value.
+bool ReturnsValidPath(int dir_type) {
+ std::wstring path;
+ bool result = PathService::Get(dir_type, &path);
+ return result && !path.empty();
+}
+
+// Test that all PathService::Get calls return a value and a true result
+// in the development environment. (This test was created because a few
+// later changes to Get broke the semantics of the function and yielded the
+// correct value while returning false.)
+TEST(PathServiceTest, Get) {
+ for (int key = base::DIR_CURRENT; key < base::PATH_END; ++key) {
+ EXPECT_PRED1(ReturnsValidPath, key);
+ }
+}
diff --git a/base/pe_image.cc b/base/pe_image.cc
new file mode 100644
index 0000000..b50bca5
--- /dev/null
+++ b/base/pe_image.cc
@@ -0,0 +1,560 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file implements PEImage, a generic class to manipulate PE files.
+// This file was adapted from GreenBorder's Code.
+
+#include "base/pe_image.h"
+
+// Structure to perform imports enumerations.
+struct EnumAllImportsStorage {
+ PEImage::EnumImportsFunction callback;
+ PVOID cookie;
+};
+
+// Callback used to enumerate imports. See EnumImportChunksFunction.
+bool ProcessImportChunk(const PEImage &image, LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie) {
+ EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+ cookie);
+
+ return image.EnumOneImportChunk(storage.callback, module, name_table, iat,
+ storage.cookie);
+}
+
+// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction.
+bool ProcessDelayImportChunk(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module, PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat, PVOID cookie) {
+ EnumAllImportsStorage &storage = *reinterpret_cast<EnumAllImportsStorage*>(
+ cookie);
+
+ return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor,
+ module, name_table, iat, bound_iat,
+ unload_iat, storage.cookie);
+}
+
+void PEImage::set_module(HMODULE module) {
+ module_ = module;
+}
+
+PIMAGE_DOS_HEADER PEImage::GetDosHeader() const {
+ return reinterpret_cast<PIMAGE_DOS_HEADER>(module_);
+}
+
+PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ return reinterpret_cast<PIMAGE_NT_HEADERS>(
+ reinterpret_cast<char*>(dos_header) + dos_header->e_lfanew);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers);
+
+ if (section < nt_headers->FileHeader.NumberOfSections)
+ return first_section + section;
+ else
+ return NULL;
+}
+
+WORD PEImage::GetNumSections() const {
+ return GetNTHeaders()->FileHeader.NumberOfSections;
+}
+
+DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ return nt_headers->OptionalHeader.DataDirectory[directory].Size;
+}
+
+PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ return RVAToAddr(
+ nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress);
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const {
+ PBYTE target = reinterpret_cast<PBYTE>(address);
+ PIMAGE_SECTION_HEADER section;
+
+ for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) {
+ // Don't use the virtual RVAToAddr.
+ PBYTE start = reinterpret_cast<PBYTE>(
+ PEImage::RVAToAddr(section->VirtualAddress));
+
+ DWORD size = section->Misc.VirtualSize;
+
+ if ((start <= target) && (start + size > target))
+ return section;
+ }
+
+ return NULL;
+}
+
+PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName(
+ LPCSTR section_name) const {
+ if (NULL == section_name)
+ return NULL;
+
+ PIMAGE_SECTION_HEADER ret = NULL;
+ int num_sections = GetNumSections();
+
+ for (int i = 0; i < num_sections; i++) {
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(i);
+ if (0 == _strnicmp(reinterpret_cast<LPCSTR>(section->Name), section_name,
+ sizeof(section->Name))) {
+ ret = section;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+PDWORD PEImage::GetExportEntry(LPCSTR name) const {
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (NULL == exports)
+ return NULL;
+
+ WORD ordinal = 0;
+ if (!GetProcOrdinal(name, &ordinal))
+ return NULL;
+
+ PDWORD functions = reinterpret_cast<PDWORD>(
+ RVAToAddr(exports->AddressOfFunctions));
+
+ return functions + ordinal - exports->Base;
+}
+
+FARPROC PEImage::GetProcAddress(LPCSTR function_name) const {
+ PDWORD export_entry = GetExportEntry(function_name);
+ if (NULL == export_entry)
+ return NULL;
+
+ PBYTE function = reinterpret_cast<PBYTE>(RVAToAddr(*export_entry));
+
+ PBYTE exports = reinterpret_cast<PBYTE>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // Check for forwarded exports as a special case.
+ if (exports <= function && exports + size > function)
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // This cast generates a warning because it is 32 bit specific.
+ return reinterpret_cast<FARPROC>(0xFFFFFFFF);
+#pragma warning(pop)
+
+ return reinterpret_cast<FARPROC>(function);
+}
+
+bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const {
+ if (NULL == ordinal)
+ return false;
+
+ PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory();
+
+ if (NULL == exports)
+ return false;
+
+ if (IsOrdinal(function_name)) {
+ *ordinal = ToOrdinal(function_name);
+ } else {
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PDWORD lower = names;
+ PDWORD upper = names + exports->NumberOfNames;
+ int cmp = -1;
+
+ // Binary Search for the name.
+ while (lower != upper) {
+ PDWORD middle = lower + (upper - lower) / 2;
+ LPCSTR name = reinterpret_cast<LPCSTR>(RVAToAddr(*middle));
+
+ cmp = strcmp(function_name, name);
+
+ if (cmp == 0) {
+ lower = middle;
+ break;
+ }
+
+ if (cmp > 0)
+ lower = middle + 1;
+ else
+ upper = middle;
+ }
+
+ if (cmp != 0)
+ return false;
+
+
+ PWORD ordinals = reinterpret_cast<PWORD>(
+ RVAToAddr(exports->AddressOfNameOrdinals));
+
+ *ordinal = ordinals[lower - names] + static_cast<WORD>(exports->Base);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const {
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+ UINT num_sections = nt_headers->FileHeader.NumberOfSections;
+ PIMAGE_SECTION_HEADER section = GetSectionHeader(0);
+
+ for (UINT i = 0; i < num_sections; i++, section++) {
+ PVOID section_start = RVAToAddr(section->VirtualAddress);
+ DWORD size = section->Misc.VirtualSize;
+
+ if (!callback(*this, section, section_start, size, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT);
+
+ // Check if there are any exports at all.
+ if (NULL == directory || 0 == size)
+ return true;
+
+ PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+ directory);
+ UINT ordinal_base = exports->Base;
+ UINT num_funcs = exports->NumberOfFunctions;
+ UINT num_names = exports->NumberOfNames;
+ PDWORD functions = reinterpret_cast<PDWORD>(RVAToAddr(
+ exports->AddressOfFunctions));
+ PDWORD names = reinterpret_cast<PDWORD>(RVAToAddr(exports->AddressOfNames));
+ PWORD ordinals = reinterpret_cast<PWORD>(RVAToAddr(
+ exports->AddressOfNameOrdinals));
+
+ for (UINT count = 0; count < num_funcs; count++) {
+ PVOID func = RVAToAddr(functions[count]);
+ if (NULL == func)
+ continue;
+
+ // Check for a name.
+ LPCSTR name = NULL;
+ UINT hint;
+ for (hint = 0; hint < num_names; hint++) {
+ if (ordinals[hint] == count) {
+ name = reinterpret_cast<LPCSTR>(RVAToAddr(names[hint]));
+ break;
+ }
+ }
+
+ if (name == NULL)
+ hint = 0;
+
+ // Check for forwarded exports.
+ LPCSTR forward = NULL;
+ if (reinterpret_cast<char*>(func) >= reinterpret_cast<char*>(directory) &&
+ reinterpret_cast<char*>(func) <= reinterpret_cast<char*>(directory) +
+ size) {
+ forward = reinterpret_cast<LPCSTR>(func);
+ func = 0;
+ }
+
+ if (!callback(*this, ordinal_base + count, hint, name, func, forward,
+ cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC);
+ PIMAGE_BASE_RELOCATION base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+ directory);
+
+ if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION))
+ return true;
+
+ while (base->SizeOfBlock) {
+ PWORD reloc = reinterpret_cast<PWORD>(base + 1);
+ UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) /
+ sizeof(WORD);
+
+ for (UINT i = 0; i < num_relocs; i++, reloc++) {
+ WORD type = *reloc >> 12;
+ PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF));
+
+ if (!callback(*this, type, address, cookie))
+ return false;
+ }
+
+ base = reinterpret_cast<PIMAGE_BASE_RELOCATION>(
+ reinterpret_cast<char*>(base) + base->SizeOfBlock);
+ }
+
+ return true;
+}
+
+bool PEImage::EnumImportChunks(EnumImportChunksFunction callback,
+ PVOID cookie) const {
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT);
+ PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk();
+
+ if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR))
+ return true;
+
+ for (; import->FirstThunk; import++) {
+ LPCSTR module_name = reinterpret_cast<LPCSTR>(RVAToAddr(import->Name));
+ PIMAGE_THUNK_DATA name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(import->OriginalFirstThunk));
+ PIMAGE_THUNK_DATA iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(import->FirstThunk));
+
+ if (!callback(*this, module_name, name_table, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneImportChunk(EnumImportsFunction callback,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie) const {
+ if (NULL == name_table)
+ return false;
+
+ for (; name_table && name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = NULL;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const {
+ EnumAllImportsStorage temp = { callback, cookie };
+ return EnumImportChunks(ProcessImportChunk, &temp);
+}
+
+bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie) const {
+ PVOID directory = GetImageDirectoryEntryAddr(
+ IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
+ PImgDelayDescr delay_descriptor = reinterpret_cast<PImgDelayDescr>(directory);
+
+ if (directory == NULL || size == 0)
+ return true;
+
+ for (; delay_descriptor->rvaHmod; delay_descriptor++) {
+ PIMAGE_THUNK_DATA name_table;
+ PIMAGE_THUNK_DATA iat;
+ PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT
+ PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT
+ LPCSTR module_name;
+
+ // check if VC7-style imports, using RVAs instead of
+ // VC6-style addresses.
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ module_name = reinterpret_cast<LPCSTR>(
+ RVAToAddr(delay_descriptor->rvaDLLName));
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaINT));
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaIAT));
+ bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaBoundIAT));
+ unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ RVAToAddr(delay_descriptor->rvaUnloadIAT));
+ } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // These casts generate warnings because they are 32 bit specific.
+ module_name = reinterpret_cast<LPCSTR>(delay_descriptor->rvaDLLName);
+ name_table = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaINT);
+ iat = reinterpret_cast<PIMAGE_THUNK_DATA>(delay_descriptor->rvaIAT);
+ bound_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaBoundIAT);
+ unload_iat = reinterpret_cast<PIMAGE_THUNK_DATA>(
+ delay_descriptor->rvaUnloadIAT);
+#pragma warning(pop)
+ }
+
+ if (!callback(*this, delay_descriptor, module_name, name_table, iat,
+ bound_iat, unload_iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) const {
+ UNREFERENCED_PARAMETER(bound_iat);
+ UNREFERENCED_PARAMETER(unload_iat);
+
+ for (; name_table->u1.Ordinal; name_table++, iat++) {
+ LPCSTR name = NULL;
+ WORD ordinal = 0;
+ WORD hint = 0;
+
+ if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) {
+ ordinal = static_cast<WORD>(IMAGE_ORDINAL32(name_table->u1.Ordinal));
+ } else {
+ PIMAGE_IMPORT_BY_NAME import;
+ bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0;
+
+ if (rvas) {
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ RVAToAddr(name_table->u1.ForwarderString));
+ } else {
+#pragma warning(push)
+#pragma warning(disable: 4312)
+ // This cast generates a warning because it is 32 bit specific.
+ import = reinterpret_cast<PIMAGE_IMPORT_BY_NAME>(
+ name_table->u1.ForwarderString);
+#pragma warning(pop)
+ }
+
+ hint = import->Hint;
+ name = reinterpret_cast<LPCSTR>(&import->Name);
+ }
+
+ if (!callback(*this, module_name, ordinal, name, hint, iat, cookie))
+ return false;
+ }
+
+ return true;
+}
+
+bool PEImage::EnumAllDelayImports(EnumImportsFunction callback,
+ PVOID cookie) const {
+ EnumAllImportsStorage temp = { callback, cookie };
+ return EnumDelayImportChunks(ProcessDelayImportChunk, &temp);
+}
+
+bool PEImage::VerifyMagic() const {
+ PIMAGE_DOS_HEADER dos_header = GetDosHeader();
+
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ return false;
+
+ PIMAGE_NT_HEADERS nt_headers = GetNTHeaders();
+
+ if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
+ return false;
+
+ if (nt_headers->FileHeader.SizeOfOptionalHeader !=
+ sizeof(IMAGE_OPTIONAL_HEADER))
+ return false;
+
+ if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+ return false;
+
+ return true;
+}
+
+bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const {
+ LPVOID address = RVAToAddr(rva);
+ return ImageAddrToOnDiskOffset(address, on_disk_offset);
+}
+
+bool PEImage::ImageAddrToOnDiskOffset(LPVOID address,
+ DWORD *on_disk_offset) const {
+ if (NULL == address)
+ return false;
+
+ // Get the section that this address belongs to.
+ PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address);
+ if (NULL == section_header)
+ return false;
+
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // These casts generate warnings because they are 32 bit specific.
+ // Don't follow the virtual RVAToAddr, use the one on the base.
+ DWORD offset_within_section = reinterpret_cast<DWORD>(address) -
+ reinterpret_cast<DWORD>(PEImage::RVAToAddr(
+ section_header->VirtualAddress));
+#pragma warning(pop)
+
+ *on_disk_offset = section_header->PointerToRawData + offset_within_section;
+ return true;
+}
+
+PVOID PEImage::RVAToAddr(DWORD rva) const {
+ if (rva == 0)
+ return NULL;
+
+ return reinterpret_cast<char*>(module_) + rva;
+}
+
+PVOID PEImageAsData::RVAToAddr(DWORD rva) const {
+ if (rva == 0)
+ return NULL;
+
+ PVOID in_memory = PEImage::RVAToAddr(rva);
+ DWORD dummy;
+
+ if (!ImageAddrToOnDiskOffset(in_memory, &dummy))
+ return NULL;
+
+ return in_memory;
+}
diff --git a/base/pe_image.h b/base/pe_image.h
new file mode 100644
index 0000000..a8ff941
--- /dev/null
+++ b/base/pe_image.h
@@ -0,0 +1,282 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file was adapted from GreenBorder's Code.
+// To understand what this class is about (for other than well known functions
+// as GetProcAddress), a good starting point is "An In-Depth Look into the
+// Win32 Portable Executable File Format" by Matt Pietrek:
+// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx
+
+#ifndef BASE_SRC_PE_IMAGE_H__
+#define BASE_SRC_PE_IMAGE_H__
+
+#include <windows.h>
+#include <DelayIMP.h>
+
+// This class is a wrapper for the Portable Executable File Format (PE).
+// It's main purpose is to provide an easy way to work with imports and exports
+// from a file, mapped in memory as image.
+class PEImage {
+ public:
+ // Callback to enumerate sections.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumSectionsFunction)(const PEImage &image,
+ PIMAGE_SECTION_HEADER header,
+ PVOID section_start, DWORD section_size,
+ PVOID cookie);
+
+ // Callback to enumerate exports.
+ // function is the actual address of the symbol. If forward is not null, it
+ // contains the dll and symbol to forward this export to. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumExportsFunction)(const PEImage &image, DWORD ordinal,
+ DWORD hint, LPCSTR name, PVOID function,
+ LPCSTR forward, PVOID cookie);
+
+ // Callback to enumerate import blocks.
+ // name_table and iat point to the imports name table and address table for
+ // this block. cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumImportChunksFunction)(const PEImage &image, LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+ // Callback to enumerate imports.
+ // module is the dll that exports this symbol. cookie is the value passed to
+ // the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumImportsFunction)(const PEImage &image, LPCSTR module,
+ DWORD ordinal, LPCSTR name, DWORD hint,
+ PIMAGE_THUNK_DATA iat, PVOID cookie);
+
+ // Callback to enumerate dalayed import blocks.
+ // module is the dll that exports this block of symbols. cookie is the value
+ // passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumDelayImportChunksFunction)(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie);
+
+ // Callback to enumerate relocations.
+ // cookie is the value passed to the enumerate method.
+ // Returns true to continue the enumeration.
+ typedef bool (*EnumRelocsFunction)(const PEImage &image, WORD type,
+ PVOID address, PVOID cookie);
+
+ explicit PEImage(HMODULE module) : module_(module) {}
+ explicit PEImage(const void* module) {
+ module_ = reinterpret_cast<HMODULE>(const_cast<void*>(module));
+ }
+
+ // Gets the HMODULE for this object.
+ HMODULE module() const;
+
+ // Sets this object's HMODULE.
+ void set_module(HMODULE module);
+
+ // Checks if this symbol is actually an ordinal.
+ static bool IsOrdinal(LPCSTR name);
+
+ // Converts a named symbol to the corresponding ordinal.
+ static WORD ToOrdinal(LPCSTR name);
+
+ // Returns the DOS_HEADER for this PE.
+ PIMAGE_DOS_HEADER GetDosHeader() const;
+
+ // Returns the NT_HEADER for this PE.
+ PIMAGE_NT_HEADERS GetNTHeaders() const;
+
+ // Returns number of sections of this PE.
+ WORD GetNumSections() const;
+
+ // Returns the header for a given section.
+ // returns NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const;
+
+ // Returns the size of a given directory entry.
+ DWORD GetImageDirectoryEntrySize(UINT directory) const;
+
+ // Returns the address of a given directory entry.
+ PVOID GetImageDirectoryEntryAddr(UINT directory) const;
+
+ // Returns the section header for a given address.
+ // Use: s = image.GetImageSectionFromAddr(a);
+ // Post: 's' is the section header of the section that contains 'a'
+ // or NULL if there is no such section.
+ PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const;
+
+ // Returns the section header for a given section.
+ PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const;
+
+ // Returns the first block of imports.
+ PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const;
+
+ // Returns the exports directory.
+ PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const;
+
+ // Returns a given export entry.
+ // Use: e = image.GetExportEntry(f);
+ // Pre: 'f' is either a zero terminated string or ordinal
+ // Post: 'e' is a pointer to the export directory entry
+ // that contains 'f's export RVA, or NULL if 'f'
+ // is not exported from this image
+ PDWORD GetExportEntry(LPCSTR name) const;
+
+ // Returns the address for a given exported symbol.
+ // Use: p = image.GetProcAddress(f);
+ // Pre: 'f' is either a zero terminated string or ordinal.
+ // Post: if 'f' is a non-forwarded export from image, 'p' is
+ // the exported function. If 'f' is a forwarded export
+ // then p is the special value 0xFFFFFFFF. In this case
+ // RVAToAddr(*GetExportEntry) can be used to resolve
+ // the string that describes the forward.
+ FARPROC GetProcAddress(LPCSTR function_name) const;
+
+ // Retrieves the ordinal for a given exported symbol.
+ // Returns true if the symbol was found.
+ bool GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const;
+
+ // Enumerates PE sections.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE exports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumExports(EnumExportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumAllImports(EnumImportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumImportChunks(EnumImportChunksFunction callback, PVOID cookie) const;
+
+ // Enumerates the imports from a single PE import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneImportChunk(EnumImportsFunction callback, LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat,
+ PVOID cookie) const;
+
+
+ // Enumerates PE delay imports.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumAllDelayImports(EnumImportsFunction callback, PVOID cookie) const;
+
+ // Enumerates PE delay import blocks.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback,
+ PVOID cookie) const;
+
+ // Enumerates imports from a single PE delay import block.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumOneDelayImportChunk(EnumImportsFunction callback,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module_name,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) const;
+
+ // Enumerates PE relocation entries.
+ // cookie is a generic cookie to pass to the callback.
+ // Returns true on success.
+ bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const;
+
+ // Verifies the magic values on the PE file.
+ // Returns true if all values are correct.
+ bool VerifyMagic() const;
+
+ // Converts an rva value to the appropriate address.
+ virtual PVOID RVAToAddr(DWORD rva) const;
+
+ // Converts an rva value to an offset on disk.
+ // Returns true on success.
+ bool ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const;
+
+ // Converts an address to an offset on disk.
+ // Returns true on success.
+ bool ImageAddrToOnDiskOffset(LPVOID address, DWORD *on_disk_offset) const;
+
+ private:
+ HMODULE module_;
+};
+
+// This class is an extension to the PEImage class that allows working with PE
+// files mapped as data instead of as image file.
+class PEImageAsData : public PEImage {
+ public:
+ explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {}
+
+ virtual PVOID RVAToAddr(DWORD rva) const;
+};
+
+inline bool PEImage::IsOrdinal(LPCSTR name) {
+#pragma warning(push)
+#pragma warning(disable: 4311)
+ // This cast generates a warning because it is 32 bit specific.
+ return reinterpret_cast<DWORD>(name) <= 0xFFFF;
+#pragma warning(pop)
+}
+
+inline WORD PEImage::ToOrdinal(LPCSTR name) {
+ return reinterpret_cast<WORD>(name);
+}
+
+inline HMODULE PEImage::module() const {
+ return module_;
+}
+
+inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const {
+ return reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT));
+}
+
+inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const {
+ return reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>(
+ GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT));
+}
+
+#endif // BASE_SRC_PE_IMAGE_H__
diff --git a/base/pe_image_unittest.cc b/base/pe_image_unittest.cc
new file mode 100644
index 0000000..31c99a5
--- /dev/null
+++ b/base/pe_image_unittest.cc
@@ -0,0 +1,226 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains unit tests for PEImage.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/pe_image.h"
+
+// Just counts the number of invocations.
+bool ExportsCallback(const PEImage &image,
+ DWORD ordinal,
+ DWORD hint,
+ LPCSTR name,
+ PVOID function,
+ LPCSTR forward,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool ImportsCallback(const PEImage &image,
+ LPCSTR module,
+ DWORD ordinal,
+ LPCSTR name,
+ DWORD hint,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool SectionsCallback(const PEImage &image,
+ PIMAGE_SECTION_HEADER header,
+ PVOID section_start,
+ DWORD section_size,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool RelocsCallback(const PEImage &image,
+ WORD type,
+ PVOID address,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool ImportChunksCallback(const PEImage &image,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// Just counts the number of invocations.
+bool DelayImportChunksCallback(const PEImage &image,
+ PImgDelayDescr delay_descriptor,
+ LPCSTR module,
+ PIMAGE_THUNK_DATA name_table,
+ PIMAGE_THUNK_DATA iat,
+ PIMAGE_THUNK_DATA bound_iat,
+ PIMAGE_THUNK_DATA unload_iat,
+ PVOID cookie) {
+ int* count = reinterpret_cast<int*>(cookie);
+ (*count)++;
+ return true;
+}
+
+// We'll be using some known values for the tests.
+enum Value {
+ sections = 0,
+ imports_dlls,
+ delay_dlls,
+ exports,
+ imports,
+ delay_imports,
+ relocs
+};
+
+// Retrieves the expected value from advapi32.dll based on the OS.
+int GetExpectedValue(Value value, DWORD os) {
+ const int xp_delay_dlls = 2;
+ const int xp_exports = 675;
+ const int xp_imports = 422;
+ const int xp_delay_imports = 8;
+ const int xp_relocs = 9180;
+ const int vista_delay_dlls = 4;
+ const int vista_exports = 799;
+ const int vista_imports = 476;
+ const int vista_delay_imports = 24;
+ const int vista_relocs = 10188;
+ const int w2k_delay_dlls = 0;
+ const int w2k_exports = 566;
+ const int w2k_imports = 357;
+ const int w2k_delay_imports = 0;
+ const int w2k_relocs = 7388;
+
+ // Contains the expected value, for each enumerated property (Value), and the
+ // OS version: [Value][os_version]
+ const int expected[][3] = {
+ {4, 4, 4},
+ {3, 3, 3},
+ {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls},
+ {w2k_exports, xp_exports, vista_exports},
+ {w2k_imports, xp_imports, vista_imports},
+ {w2k_delay_imports, xp_delay_imports, vista_delay_imports},
+ {w2k_relocs, xp_relocs, vista_relocs}
+ };
+
+ if (value > relocs)
+ return 0;
+ if (50 == os)
+ os = 0; // 5.0
+ else if (51 == os || 52 == os)
+ os = 1;
+ else if (os >= 60)
+ os = 2; // 6.x
+ else
+ return 0;
+
+ return expected[value][os];
+}
+
+// Tests that we are able to enumerate stuff from a PE file, and that
+// the actual number of items found is within the expected range.
+TEST(PEImageTest, EnumeratesPE) {
+ HMODULE module = LoadLibrary(L"advapi32.dll");
+ ASSERT_TRUE(NULL != module);
+
+ PEImage pe(module);
+ int count = 0;
+ EXPECT_TRUE(pe.VerifyMagic());
+
+ DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion;
+ os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion;
+
+ pe.EnumSections(SectionsCallback, &count);
+ EXPECT_EQ(GetExpectedValue(sections, os), count);
+
+ count = 0;
+ pe.EnumImportChunks(ImportChunksCallback, &count);
+ EXPECT_EQ(GetExpectedValue(imports_dlls, os), count);
+
+ count = 0;
+ pe.EnumDelayImportChunks(DelayImportChunksCallback, &count);
+ EXPECT_EQ(GetExpectedValue(delay_dlls, os), count);
+
+ count = 0;
+ pe.EnumExports(ExportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(exports, os) - 20);
+ EXPECT_LT(count, GetExpectedValue(exports, os) + 100);
+
+ count = 0;
+ pe.EnumAllImports(ImportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(imports, os) - 20);
+ EXPECT_LT(count, GetExpectedValue(imports, os) + 100);
+
+ count = 0;
+ pe.EnumAllDelayImports(ImportsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2);
+ EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8);
+
+ count = 0;
+ pe.EnumRelocs(RelocsCallback, &count);
+ EXPECT_GT(count, GetExpectedValue(relocs, os) - 150);
+ EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500);
+
+ FreeLibrary(module);
+}
+
+// Tests that we can locate an specific exported symbol, by name and by ordinal.
+TEST(PEImageTest, RetrievesExports) {
+ HMODULE module = LoadLibrary(L"advapi32.dll");
+ ASSERT_TRUE(NULL != module);
+
+ PEImage pe(module);
+ WORD ordinal;
+
+ EXPECT_TRUE(pe.GetProcOrdinal("RegEnumKeyExW", &ordinal));
+
+ FARPROC address1 = pe.GetProcAddress("RegEnumKeyExW");
+ FARPROC address2 = pe.GetProcAddress(reinterpret_cast<char*>(ordinal));
+ EXPECT_TRUE(address1 != NULL);
+ EXPECT_TRUE(address2 != NULL);
+ EXPECT_TRUE(address1 == address2);
+
+ FreeLibrary(module);
+}
diff --git a/base/perftimer.cc b/base/perftimer.cc
new file mode 100644
index 0000000..442c4d3
--- /dev/null
+++ b/base/perftimer.cc
@@ -0,0 +1,69 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdio.h>
+#include <shlwapi.h>
+#include <windows.h>
+
+#include "base/perftimer.h"
+
+#include "base/logging.h"
+#include "base/basictypes.h"
+
+static FILE* perf_log_file = NULL;
+
+bool InitPerfLog(const char* log_file) {
+ if (perf_log_file) {
+ // trying to initialize twice
+ NOTREACHED();
+ return false;
+ }
+
+ return fopen_s(&perf_log_file, log_file, "w") == 0;
+}
+
+void FinalizePerfLog() {
+ if (!perf_log_file) {
+ // trying to cleanup without initializing
+ NOTREACHED();
+ return;
+ }
+ fclose(perf_log_file);
+}
+
+void LogPerfResult(const char* test_name, double value, const char* units) {
+ if (!perf_log_file) {
+ NOTREACHED();
+ return;
+ }
+
+ fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units);
+ printf("%s\t%g\t%s\n", test_name, value, units);
+}
+
diff --git a/base/perftimer.h b/base/perftimer.h
new file mode 100644
index 0000000..03a7713
--- /dev/null
+++ b/base/perftimer.h
@@ -0,0 +1,105 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PERFTTIMER_H__
+#define BASE_PERFTTIMER_H__
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/time.h"
+
+// ----------------------------------------------------------------------
+// Initializes and finalizes the perf log. These functions should be
+// called at the beginning and end (respectively) of running all the
+// performance tests. The init function returns true on success.
+// ----------------------------------------------------------------------
+bool InitPerfLog(const char* log_file);
+void FinalizePerfLog();
+
+// ----------------------------------------------------------------------
+// LogPerfResult
+// Writes to the perf result log the given 'value' resulting from the
+// named 'test'. The units are to aid in reading the log by people.
+// ----------------------------------------------------------------------
+void LogPerfResult(const char* test_name, double value, const char* units);
+
+// ----------------------------------------------------------------------
+// PerfTimer
+// A simple wrapper around Now()
+// ----------------------------------------------------------------------
+class PerfTimer {
+ public:
+ PerfTimer() {
+ begin_ = TimeTicks::Now();
+ }
+
+ // Returns the time elapsed since object construction
+ TimeDelta Elapsed() const {
+ return TimeTicks::Now() - begin_;
+ }
+
+ private:
+ TimeTicks begin_;
+};
+
+// ----------------------------------------------------------------------
+// PerfTimeLogger
+// Automates calling LogPerfResult for the common case where you want
+// to measure the time that something took. Call Done() when the test
+// is complete if you do extra work after the test or there are stack
+// objects with potentially expensive constructors. Otherwise, this
+// class with automatically log on destruction.
+// ----------------------------------------------------------------------
+class PerfTimeLogger {
+ public:
+ explicit PerfTimeLogger(const char* test_name)
+ : logged_(false),
+ test_name_(test_name) {
+ }
+
+ ~PerfTimeLogger() {
+ if (!logged_)
+ Done();
+ }
+
+ void Done() {
+ // we use a floating-point millisecond value because it is more
+ // intuitive than microseconds and we want more precision than
+ // integer milliseconds
+ LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms");
+ logged_ = true;
+ }
+
+ private:
+ bool logged_;
+ std::string test_name_;
+ PerfTimer timer_;
+};
+
+#endif // BASE_PERFTTIMER_H__
diff --git a/base/pickle.cc b/base/pickle.cc
new file mode 100644
index 0000000..a16368b
--- /dev/null
+++ b/base/pickle.cc
@@ -0,0 +1,348 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <string>
+
+#include "base/pickle.h"
+
+//------------------------------------------------------------------------------
+
+// static
+const int Pickle::kPayloadUnit = 64;
+
+// Payload is uint32 aligned.
+
+Pickle::Pickle()
+ : header_(NULL),
+ header_size_(sizeof(Header)),
+ capacity_(0),
+ variable_buffer_offset_(0) {
+ Resize(kPayloadUnit);
+ header_->payload_size = 0;
+}
+
+Pickle::Pickle(int header_size)
+ : header_(NULL),
+ header_size_(AlignInt(header_size, sizeof(uint32))),
+ capacity_(0),
+ variable_buffer_offset_(0) {
+ DCHECK(header_size >= sizeof(Header));
+ DCHECK(header_size <= kPayloadUnit);
+ Resize(kPayloadUnit);
+ header_->payload_size = 0;
+}
+
+Pickle::Pickle(const char* data, int data_len)
+ : header_(reinterpret_cast<Header*>(const_cast<char*>(data))),
+ header_size_(data_len - header_->payload_size),
+ capacity_(-1),
+ variable_buffer_offset_(0) {
+ DCHECK(header_size_ >= sizeof(Header));
+ DCHECK(header_size_ == AlignInt(header_size_, sizeof(uint32)));
+}
+
+Pickle::Pickle(const Pickle& other)
+ : header_(NULL),
+ header_size_(other.header_size_),
+ capacity_(0),
+ variable_buffer_offset_(other.variable_buffer_offset_) {
+ size_t payload_size = header_size_ + other.header_->payload_size;
+ bool resized = Resize(payload_size);
+ CHECK(resized); // Realloc failed.
+ memcpy(header_, other.header_, payload_size);
+}
+
+Pickle::~Pickle() {
+ if (capacity_ != -1)
+ free(header_);
+}
+
+Pickle& Pickle::operator=(const Pickle& other) {
+ if (header_size_ != other.header_size_ && capacity_ != -1) {
+ free(header_);
+ header_ = NULL;
+ header_size_ = other.header_size_;
+ }
+ bool resized = Resize(other.header_size_ + other.header_->payload_size);
+ CHECK(resized); // Realloc failed.
+ memcpy(header_, other.header_, header_size_ + other.header_->payload_size);
+ variable_buffer_offset_ = other.variable_buffer_offset_;
+ return *this;
+}
+
+bool Pickle::ReadBool(void** iter, bool* result) const {
+ DCHECK(iter);
+
+ int tmp;
+ if (!ReadInt(iter, &tmp))
+ return false;
+ DCHECK(0 == tmp || 1 == tmp);
+ *result = tmp ? true : false;
+ return true;
+}
+
+bool Pickle::ReadInt(void** iter, int* result) const {
+ DCHECK(iter);
+ if (!*iter)
+ *iter = const_cast<char*>(payload());
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return false;
+
+ // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on
+ // alignment.
+ // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
+ *result = *reinterpret_cast<int*>(*iter);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadLength(void** iter, int* result) const {
+ if (!ReadInt(iter, result))
+ return false;
+ return ((*result) >= 0);
+}
+
+bool Pickle::ReadSize(void** iter, size_t* result) const {
+ DCHECK(iter);
+ if (!*iter)
+ *iter = const_cast<char*>(payload());
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return false;
+
+ // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on
+ // alignment.
+ // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result));
+ *result = *reinterpret_cast<size_t*>(*iter);
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadInt64(void** iter, int64* result) const {
+ DCHECK(iter);
+ if (!*iter)
+ *iter = const_cast<char*>(payload());
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return false;
+
+ memcpy(result, *iter, sizeof(*result));
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadIntPtr(void** iter, intptr_t* result) const {
+ DCHECK(iter);
+ if (!*iter)
+ *iter = const_cast<char*>(payload());
+
+ if (!IteratorHasRoomFor(*iter, sizeof(*result)))
+ return false;
+
+ memcpy(result, *iter, sizeof(*result));
+
+ UpdateIter(iter, sizeof(*result));
+ return true;
+}
+
+bool Pickle::ReadString(void** iter, std::string* result) const {
+ DCHECK(iter);
+
+ int len;
+ if (!ReadLength(iter, &len))
+ return false;
+ if (!IteratorHasRoomFor(*iter, len))
+ return false;
+
+ char* chars = reinterpret_cast<char*>(*iter);
+ result->assign(chars, len);
+
+ UpdateIter(iter, len);
+ return true;
+}
+
+bool Pickle::ReadWString(void** iter, std::wstring* result) const {
+ DCHECK(iter);
+
+ int len;
+ if (!ReadLength(iter, &len))
+ return false;
+ if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t)))
+ return false;
+
+ wchar_t* chars = reinterpret_cast<wchar_t*>(*iter);
+ result->assign(chars, len);
+
+ UpdateIter(iter, len * sizeof(wchar_t));
+ return true;
+}
+
+bool Pickle::ReadBytes(void** iter, const char** data, int length) const {
+ DCHECK(iter);
+ DCHECK(data);
+
+ if (!IteratorHasRoomFor(*iter, length))
+ return false;
+
+ *data = reinterpret_cast<const char*>(*iter);
+
+ UpdateIter(iter, length);
+ return true;
+}
+
+bool Pickle::ReadData(void** iter, const char** data, int* length) const {
+ DCHECK(iter);
+ DCHECK(data);
+ DCHECK(length);
+
+ if (!ReadLength(iter, length))
+ return false;
+
+ return ReadBytes(iter, data, *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;
+ if (header_size_ + new_size > capacity_ && !Resize(header_size_ + new_size))
+ return NULL;
+
+ header_->payload_size = new_size;
+ return payload() + offset;
+}
+
+void Pickle::EndWrite(char* dest, int length) {
+ // Zero-pad to keep tools like purify from complaining about uninitialized
+ // memory.
+ if (length % sizeof(uint32))
+ memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32)));
+}
+
+bool Pickle::WriteBytes(const void* data, int data_len) {
+ DCHECK(capacity_ != -1) << "oops: pickle is readonly";
+
+ char* dest = BeginWrite(data_len);
+ if (!dest)
+ return false;
+
+ memcpy(dest, data, data_len);
+
+ EndWrite(dest, data_len);
+ return true;
+}
+
+bool Pickle::WriteString(const std::string& value) {
+ if (!WriteInt(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(), static_cast<int>(value.size()));
+}
+
+bool Pickle::WriteWString(const std::wstring& value) {
+ if (!WriteInt(static_cast<int>(value.size())))
+ return false;
+
+ return WriteBytes(value.data(),
+ static_cast<int>(value.size() * sizeof(value.data()[0])));
+}
+
+bool Pickle::WriteData(const char* data, int length) {
+ return WriteInt(length) && WriteBytes(data, length);
+}
+
+char* Pickle::BeginWriteData(int length) {
+ DCHECK_EQ(variable_buffer_offset_, 0) <<
+ "There can only be one variable buffer in a Pickle";
+
+ if (!WriteInt(length))
+ return false;
+
+ 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 length) {
+ DCHECK(variable_buffer_offset_ != 0);
+
+ VariableLengthBuffer *buffer = reinterpret_cast<VariableLengthBuffer*>(
+ reinterpret_cast<char*>(header_) + variable_buffer_offset_);
+
+ DCHECK_GE(buffer->length, length);
+
+ int old_length = buffer->length;
+ int trimmed_bytes = old_length - length;
+ if (trimmed_bytes > 0) {
+ header_->payload_size -= trimmed_bytes;
+ buffer->length = length;
+ }
+}
+
+bool Pickle::Resize(size_t new_capacity) {
+ new_capacity = AlignInt(new_capacity, kPayloadUnit);
+
+ void* p = realloc(header_, new_capacity);
+ if (!p)
+ return false;
+
+ header_ = reinterpret_cast<Header*>(p);
+ capacity_ = new_capacity;
+ return true;
+}
+
+// static
+const char* Pickle::FindNext(size_t header_size,
+ const char* start,
+ const char* end) {
+ DCHECK(header_size == AlignInt(header_size, sizeof(uint32)));
+ DCHECK(header_size <= kPayloadUnit);
+
+ const Header* hdr = reinterpret_cast<const Header*>(start);
+ const char* payload_base = start + header_size;
+ const char* payload_end = payload_base + hdr->payload_size;
+ if (payload_end < payload_base)
+ return NULL;
+
+ return (payload_end > end) ? NULL : payload_end;
+}
diff --git a/base/pickle.h b/base/pickle.h
new file mode 100644
index 0000000..5e97ff5
--- /dev/null
+++ b/base/pickle.h
@@ -0,0 +1,259 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PICKLE_H__
+#define BASE_PICKLE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+// This class provides facilities for basic binary value packing and unpacking.
+//
+// The Pickle class supports appending primitive values (ints, strings, etc.)
+// to a pickle instance. The Pickle instance grows its internal memory buffer
+// dynamically to hold the sequence of primitive values. The internal memory
+// buffer is exposed as the "data" of the Pickle. This "data" can be passed
+// to a Pickle object to initialize it for reading.
+//
+// When reading from a Pickle object, it is important for the consumer to know
+// what value types to read and in what order to read them as the Pickle does
+// not keep track of the type of data written to it.
+//
+// The Pickle's data has a header which contains the size of the Pickle's
+// payload. It can optionally support additional space in the header. That
+// space is controlled by the header_size parameter passed to the Pickle
+// constructor.
+//
+class Pickle {
+ public:
+ ~Pickle();
+
+ // Initialize a Pickle object using the default header size.
+ Pickle();
+
+ // Initialize a Pickle object with the specified header size in bytes, which
+ // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size
+ // will be rounded up to ensure that the header size is 32bit-aligned.
+ explicit Pickle(int header_size);
+
+ // Initializes a Pickle from a const block of data. The data is not copied;
+ // instead the data is merely referenced by this Pickle. Only const methods
+ // should be used on the Pickle when initialized this way. The header
+ // padding size is deduced from the data length.
+ Pickle(const char* data, int data_len);
+
+ // Initializes a Pickle as a deep copy of another Pickle.
+ Pickle(const Pickle& other);
+
+ // Performs a deep copy.
+ Pickle& operator=(const Pickle& other);
+
+ // Returns the size of the Pickle's data.
+ int size() const { return static_cast<int>(header_size_ +
+ header_->payload_size); }
+
+ // Returns the data for this Pickle.
+ const void* data() const { return header_; }
+
+ // Methods for reading the payload of the Pickle. To read from the start of
+ // the Pickle, initialize *iter to NULL. If successful, these methods return
+ // true. Otherwise, false is returned to indicate that the result could not
+ // be extracted.
+ bool ReadBool(void** iter, bool* result) const;
+ bool ReadInt(void** iter, int* result) const;
+ bool ReadSize(void** iter, size_t* result) const;
+ bool ReadInt64(void** iter, int64* result) const;
+ bool ReadIntPtr(void** iter, intptr_t* result) const;
+ bool ReadString(void** iter, std::string* result) const;
+ bool ReadWString(void** iter, std::wstring* result) const;
+ bool ReadData(void** iter, const char** data, int* length) const;
+ bool ReadBytes(void** iter, const char** data, int length) const;
+
+ // Safer version of ReadInt() checks for the result not being negative.
+ // Use it for reading the object sizes.
+ bool Pickle::ReadLength(void** iter, int* result) const;
+
+ // Methods for adding to the payload of the Pickle. These values are
+ // appended to the end of the Pickle's payload. When reading values from a
+ // Pickle, it is important to read them in the order in which they were added
+ // to the Pickle.
+ bool WriteBool(bool value) {
+ return WriteInt(value ? 1 : 0);
+ }
+ bool WriteInt(int value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteSize(size_t value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteInt64(int64 value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteIntPtr(intptr_t value) {
+ return WriteBytes(&value, sizeof(value));
+ }
+ bool WriteString(const std::string& value);
+ bool WriteWString(const std::wstring& value);
+ bool WriteData(const char* data, int length);
+ bool WriteBytes(const void* data, int data_len);
+
+ // Same as WriteData, but allows the caller to write directly into the
+ // Pickle. This saves a copy in cases where the data is not already
+ // available in a buffer. The caller should take care to not write more
+ // than the length it declares it will. Use ReadData to get the data.
+ // Returns NULL on failure.
+ //
+ // The returned pointer will only be valid until the next write operation
+ // on this Pickle.
+ char* BeginWriteData(int length);
+
+ // For Pickles which contain variable length buffers (e.g. those created
+ // with BeginWriteData), the Pickle can
+ // be 'trimmed' if the amount of data required is less than originally
+ // requested. For example, you may have created a buffer with 10K of data,
+ // but decided to only fill 10 bytes of that data. Use this function
+ // to trim the buffer so that we don't send 9990 bytes of unused data.
+ // You cannot increase the size of the variable buffer; only shrink it.
+ // This function assumes that the length of the variable buffer has
+ // not been changed.
+ void TrimWriteData(int length);
+
+ // payload follows after allocation of Header (header size is customizable)
+ struct Header {
+ size_t payload_size; // specifies the size of the payload
+ };
+
+ // Returns the header, cast to a user-specified type T. The type T must be a
+ // subclass of Header and its size must correspond to the header_size passed
+ // to the Pickle constructor.
+ template <class T>
+ T* headerT() {
+ DCHECK(sizeof(T) == header_size_);
+ return static_cast<T*>(header_);
+ }
+ template <class T>
+ const T* headerT() const {
+ DCHECK(sizeof(T) == header_size_);
+ return static_cast<const T*>(header_);
+ }
+
+ // Returns true if the given iterator could point to data with the given
+ // length. If there is no room for the given data before the end of the
+ // payload, returns false.
+ bool IteratorHasRoomFor(const void* iter, int len) const {
+ if ((len < 0) || (iter < header_) || iter > end_of_payload())
+ return false;
+ const char* end_of_region = reinterpret_cast<const char*>(iter) + len;
+ // Watch out for overflow in pointer calculation, which wraps.
+ return (iter <= end_of_region) && (end_of_region <= end_of_payload());
+ }
+
+ protected:
+ size_t payload_size() const { return header_->payload_size; }
+
+ char* payload() {
+ return reinterpret_cast<char*>(header_) + header_size_;
+ }
+ const char* payload() const {
+ return reinterpret_cast<const char*>(header_) + header_size_;
+ }
+
+ // Returns the address of the byte immediately following the currently valid
+ // header + payload.
+ char* end_of_payload() {
+ return payload() + payload_size();
+ }
+ const char* end_of_payload() const {
+ return payload() + payload_size();
+ }
+
+ size_t capacity() const {
+ return capacity_;
+ }
+
+ // Resizes the buffer for use when writing the specified amount of data. The
+ // location that the data should be written at is returned, or NULL if there
+ // was an error. Call EndWrite with the returned offset and the given length
+ // to pad out for the next write.
+ char* BeginWrite(size_t length);
+
+ // Completes the write operation by padding the data with NULL bytes until it
+ // is padded. Should be paired with BeginWrite, but it does not necessarily
+ // have to be called after the data is written.
+ void EndWrite(char* dest, int length);
+
+ // Resize the capacity, note that the input value should include the size of
+ // the header: new_capacity = sizeof(Header) + desired_payload_capacity.
+ // A realloc() failure will cause a Resize failure... and caller should check
+ // the return result for true (i.e., successful resizing).
+ bool Resize(size_t new_capacity);
+
+ // Aligns 'i' by rounding it up to the next multiple of 'alignment'
+ static inline size_t AlignInt(size_t i, int alignment) {
+ return i + (alignment - (i % alignment)) % alignment;
+ }
+
+ // Moves the iterator by the given number of bytes, making sure it is aligned.
+ // Pointer (iterator) is NOT aligned, but the change in the pointer
+ // is guaranteed to be a multiple of sizeof(uint32).
+ static inline void UpdateIter(void** iter, int bytes) {
+ *iter = static_cast<char*>(*iter) + AlignInt(bytes, sizeof(uint32));
+ }
+
+ // Find the end of the pickled data that starts at range_start. Returns NULL
+ // if the entire Pickle is not found in the given data range.
+ static const char* FindNext(size_t header_size,
+ const char* range_start,
+ const char* range_end);
+
+ // The allocation granularity of the payload.
+ static const int kPayloadUnit;
+
+ private:
+ // A buffer of variable length; used internally
+ struct VariableLengthBuffer {
+ int length;
+ char data; // This is variable length.
+ };
+
+ Header* header_;
+ size_t header_size_; // Supports extra data between header and payload.
+ // Allocation size of payload (or -1 if allocation is const).
+ size_t capacity_;
+ size_t variable_buffer_offset_; // IF non-zero, then offset to a buffer.
+
+ FRIEND_TEST(PickleTest, Resize);
+ FRIEND_TEST(PickleTest, FindNext);
+ FRIEND_TEST(PickleTest, IteratorHasRoom);
+};
+
+#endif // BASE_PICKLE_H__
diff --git a/base/pickle_unittest.cc b/base/pickle_unittest.cc
new file mode 100644
index 0000000..6780e21
--- /dev/null
+++ b/base/pickle_unittest.cc
@@ -0,0 +1,238 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <windows.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/check_handler.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/pickle.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+const int testint = 2093847192;
+const std::string teststr("Hello world"); // note non-aligned string length
+const std::wstring testwstr(L"Hello, world");
+const char testdata[] = "AAA\0BBB\0";
+const int testdatalen = arraysize(testdata) - 1;
+const bool testbool1 = false;
+const bool testbool2 = true;
+
+// checks that the result
+void VerifyResult(const Pickle& pickle) {
+ void* iter = NULL;
+
+ int outint;
+ EXPECT_TRUE(pickle.ReadInt(&iter, &outint));
+ EXPECT_EQ(testint, outint);
+
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ(teststr, outstr);
+
+ std::wstring outwstr;
+ EXPECT_TRUE(pickle.ReadWString(&iter, &outwstr));
+ EXPECT_EQ(testwstr, outwstr);
+
+ bool outbool;
+ EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+ EXPECT_EQ(testbool1, outbool);
+ EXPECT_TRUE(pickle.ReadBool(&iter, &outbool));
+ EXPECT_EQ(testbool2, outbool);
+
+ const char* outdata;
+ int outdatalen;
+ EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+ EXPECT_EQ(testdatalen, outdatalen);
+ EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+ EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen));
+ EXPECT_EQ(testdatalen, outdatalen);
+ EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0);
+
+ // reads past the end should fail
+ EXPECT_FALSE(pickle.ReadInt(&iter, &outint));
+}
+
+} // namespace
+
+TEST(PickleTest, EncodeDecode) {
+ Pickle pickle;
+
+ EXPECT_TRUE(pickle.WriteInt(testint));
+ EXPECT_TRUE(pickle.WriteString(teststr));
+ EXPECT_TRUE(pickle.WriteWString(testwstr));
+ EXPECT_TRUE(pickle.WriteBool(testbool1));
+ EXPECT_TRUE(pickle.WriteBool(testbool2));
+ EXPECT_TRUE(pickle.WriteData(testdata, testdatalen));
+
+ char* dest = pickle.BeginWriteData(testdatalen);
+ EXPECT_TRUE(dest);
+ memcpy(dest, testdata, testdatalen);
+
+ VerifyResult(pickle);
+
+ // test copy constructor
+ Pickle pickle2(pickle);
+ VerifyResult(pickle2);
+
+ // test operator=
+ Pickle pickle3;
+ pickle3 = pickle;
+ VerifyResult(pickle3);
+}
+
+TEST(PickleTest, ZeroLenStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteString(""));
+
+ void* iter = NULL;
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, ZeroLenWStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteWString(L""));
+
+ void* iter = NULL;
+ std::string outstr;
+ EXPECT_TRUE(pickle.ReadString(&iter, &outstr));
+ EXPECT_EQ("", outstr);
+}
+
+TEST(PickleTest, BadLenStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(-2));
+
+ void* iter = NULL;
+ std::string outstr;
+ EXPECT_FALSE(pickle.ReadString(&iter, &outstr));
+}
+
+TEST(PickleTest, BadLenWStr) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(-1));
+
+ void* iter = NULL;
+ std::wstring woutstr;
+ EXPECT_FALSE(pickle.ReadWString(&iter, &woutstr));
+}
+
+TEST(PickleTest, FindNext) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(1));
+ EXPECT_TRUE(pickle.WriteString("Domo"));
+
+ const char* start = reinterpret_cast<const char*>(pickle.data());
+ const char* end = start + pickle.size();
+
+ EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end));
+ EXPECT_TRUE(NULL == Pickle::FindNext(pickle.header_size_, start, end - 1));
+ EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end + 1));
+}
+
+TEST(PickleTest, IteratorHasRoom) {
+ Pickle pickle;
+ EXPECT_TRUE(pickle.WriteInt(1));
+ EXPECT_TRUE(pickle.WriteInt(2));
+
+ const void* iter = 0;
+ EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, 1));
+ iter = pickle.payload();
+ EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 0));
+ EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 1));
+ EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, -1));
+ EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, sizeof(int) * 2));
+ EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, (sizeof(int) * 2) + 1));
+}
+
+TEST(PickleTest, Resize) {
+ int unit = Pickle::kPayloadUnit;
+ scoped_array<char> data(new char[unit]);
+ char* data_ptr = data.get();
+ for (int i = 0; i < unit; i++)
+ data_ptr[i] = 'G';
+
+ // construct a message that will be exactly the size of one payload unit,
+ // note that any data will have a 4-byte header indicating the size
+ const int payload_size_after_header = unit - sizeof(uint32);
+ Pickle pickle;
+ pickle.WriteData(data_ptr, payload_size_after_header - sizeof(uint32));
+ int cur_payload = payload_size_after_header;
+
+ EXPECT_EQ(pickle.capacity(), unit);
+ EXPECT_EQ(pickle.payload_size(), payload_size_after_header);
+
+ // fill out a full page (noting data header)
+ pickle.WriteData(data_ptr, unit - sizeof(uint32));
+ cur_payload += unit;
+ EXPECT_EQ(unit*2, pickle.capacity());
+ EXPECT_EQ(cur_payload, pickle.payload_size());
+
+ // one more byte should expand the capacity by one unit
+ pickle.WriteData(data_ptr, 1);
+ cur_payload += 5;
+ EXPECT_EQ(unit * 3, pickle.capacity());
+ EXPECT_EQ(cur_payload, pickle.payload_size());
+}
+
+TEST(PickleTest, HeaderPadding) {
+ struct CustomHeader : Pickle::Header {
+ int blah;
+ };
+
+ const uint32 kMagic = 0x12345678;
+
+ Pickle pickle(sizeof(CustomHeader));
+ pickle.WriteInt(kMagic);
+
+ // this should not overwrite the 'int' payload
+ pickle.headerT<CustomHeader>()->blah = 10;
+
+ void* iter = NULL;
+ int result;
+ ASSERT_TRUE(pickle.ReadInt(&iter, &result));
+
+ EXPECT_EQ(result, kMagic);
+}
+
+TEST(PickleTest, EqualsOperator) {
+ Pickle source;
+ source.WriteInt(1);
+
+ Pickle copy_refs_source_buffer(static_cast<const char*>(source.data()),
+ source.size());
+ Pickle copy;
+ copy = copy_refs_source_buffer;
+ ASSERT_EQ(source.size(), copy.size());
+} \ No newline at end of file
diff --git a/base/platform_thread.cc b/base/platform_thread.cc
new file mode 100644
index 0000000..1966ab2
--- /dev/null
+++ b/base/platform_thread.cc
@@ -0,0 +1,51 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/platform_thread.h"
+
+// static
+PlatformThread PlatformThread::Current() {
+ PlatformThread thread;
+
+#ifdef WIN32
+ thread.thread_ = GetCurrentThread();
+#else
+ thread.thread_ = pthread_self();
+#endif
+
+ return thread;
+}
+
+bool PlatformThread::operator==(const PlatformThread& other_thread) {
+#ifdef WIN32
+ return thread_ == other_thread.thread_;
+#else
+ return pthread_equal(thread_, other_thread.thread_);
+#endif
+}
diff --git a/base/platform_thread.h b/base/platform_thread.h
new file mode 100644
index 0000000..b6fb45d
--- /dev/null
+++ b/base/platform_thread.h
@@ -0,0 +1,53 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PLATFORM_THREAD_H__
+#define BASE_PLATFORM_THREAD_H__
+
+#ifdef WIN32
+#include <windows.h>
+typedef HANDLE PlatformThreadHandle;
+#else
+#include <pthread.h>
+typedef pthread_t PlatformThreadHandle;
+#endif
+
+class PlatformThread {
+ public:
+ // Gets the current thread.
+ static PlatformThread Current();
+
+ bool operator==(const PlatformThread& other_thread);
+
+ private:
+
+ PlatformThreadHandle thread_;
+};
+
+#endif // BASE_PLATFORM_THREAD_H__
diff --git a/base/port.h b/base/port.h
new file mode 100644
index 0000000..db688dc
--- /dev/null
+++ b/base/port.h
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PORT_H__
+#define BASE_PORT_H__
+
+#ifdef MSC_VER
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including <stdint.h>
+// to get the INTn_C and UINTn_C macros for integer constants. It's difficult
+// to guarantee any specific ordering of header includes, so it's difficult to
+// guarantee that the INTn_C macros can be defined by including <stdint.h> at
+// any specific point. Provide GG_INTn_C macros instead.
+
+#define GG_INT8_C(x) (x)
+#define GG_INT16_C(x) (x)
+#define GG_INT32_C(x) (x)
+#define GG_INT64_C(x) GG_LONGLONG(x)
+
+#define GG_UINT8_C(x) (x ## U)
+#define GG_UINT16_C(x) (x ## U)
+#define GG_UINT32_C(x) (x ## U)
+#define GG_UINT64_C(x) GG_ULONGLONG(x)
+
+#endif // BASE_PORT_H__
diff --git a/base/pr_time_test.cc b/base/pr_time_test.cc
new file mode 100644
index 0000000..e054164
--- /dev/null
+++ b/base/pr_time_test.cc
@@ -0,0 +1,179 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/third_party/nspr/prtime.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <time.h>
+
+namespace {
+
+const int64 kMicrosecondsPerSecond = 1000000i64;
+
+// time_t representation of 15th Oct 2007 12:45:00 PDT
+PRTime comparison_time_pdt = 1192477500 * kMicrosecondsPerSecond;
+
+// Specialized test fixture allowing time strings without timezones to be
+// tested by comparing them to a known time in the local zone.
+class PRTimeTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Use mktime to get a time_t, and turn it into a PRTime by converting
+ // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This
+ // must be a time guaranteed to be outside of a DST fallback hour in
+ // any timezone.
+ struct tm local_comparison_tm = {
+ 0, // second
+ 45, // minute
+ 12, // hour
+ 15, // day of month
+ 10 - 1, // month
+ 2007 - 1900, // year
+ 0, // day of week (ignored, output only)
+ 0, // day of year (ignored, output only)
+ -1 // DST in effect, -1 tells mktime to figure it out
+ };
+ comparison_time_local_ = mktime(&local_comparison_tm) *
+ kMicrosecondsPerSecond;
+ ASSERT_GT(comparison_time_local_, 0);
+ }
+
+ PRTime comparison_time_local_;
+};
+
+} // namespace
+
+// Tests the PR_ParseTimeString nspr helper function for
+// a variety of time strings.
+TEST_F(PRTimeTest, ParseTimeTest1) {
+ //time_t current_time = 0;
+ PRTime current_time = 0;
+ time(&current_time);
+
+ tm local_time = {0};
+ localtime_s(&local_time,&current_time);
+
+ char time_buf[MAX_PATH] = {0};
+ asctime_s(time_buf, arraysize(time_buf), &local_time);
+ current_time *= PR_USEC_PER_SEC;
+
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString(time_buf, PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(current_time,parsed_time);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest2) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest3) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest4) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest5) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest6) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest7) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest8) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE,
+ &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_local_);
+}
+
+TEST_F(PRTimeTest, ParseTimeTest9) {
+ PRTime parsed_time = 0;
+ PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)",
+ PR_FALSE, &parsed_time);
+ EXPECT_EQ(PR_SUCCESS, result);
+ EXPECT_EQ(parsed_time, comparison_time_pdt);
+}
+
+// This tests the Time::FromString wrapper over PR_ParseTimeString
+TEST_F(PRTimeTest, ParseTimeTest10) {
+ Time parsed_time;
+ bool result = Time::FromString(L"15/10/07 12:45",&parsed_time);
+ EXPECT_EQ(true, result);
+
+ time_t computed_time = parsed_time.ToTimeT();
+ time_t time_to_compare = comparison_time_local_ / kMicrosecondsPerSecond;
+ EXPECT_EQ(computed_time, time_to_compare);
+}
+
+// This tests the Time::FromString wrapper over PR_ParseTimeString
+TEST_F(PRTimeTest, ParseTimeTest11) {
+ Time parsed_time;
+ bool result = Time::FromString(L"Mon, 15 Oct 2007 19:45:00 GMT",
+ &parsed_time);
+ EXPECT_EQ(true, result);
+
+ time_t computed_time = parsed_time.ToTimeT();
+ time_t time_to_compare = comparison_time_pdt / kMicrosecondsPerSecond;
+ EXPECT_EQ(computed_time, time_to_compare);
+}
diff --git a/base/process.cc b/base/process.cc
new file mode 100644
index 0000000..462c84b
--- /dev/null
+++ b/base/process.cc
@@ -0,0 +1,134 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/process.h"
+#include "base/logging.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+
+bool Process::IsProcessBackgrounded() {
+ DCHECK(process_);
+ DWORD priority = GetPriorityClass(process_);
+ if (priority == 0)
+ return false; // Failure case.
+ return priority == BELOW_NORMAL_PRIORITY_CLASS;
+}
+
+bool Process::SetProcessBackgrounded(bool value) {
+ DCHECK(process_);
+ DWORD priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS;
+ return (SetPriorityClass(process_, priority) != 0);
+}
+
+// According to MSDN, these are the default values which XP
+// uses to govern working set soft limits.
+// http://msdn.microsoft.com/en-us/library/ms686234.aspx
+static const int kWinDefaultMinSet = 50 * 4096;
+static const int kWinDefaultMaxSet = 345 * 4096;
+static const int kDampingFactor = 2;
+
+bool Process::ReduceWorkingSet() {
+ if (!process_)
+ return false;
+ // The idea here is that when we the process' working set has gone
+ // down, we want to release those pages to the OS quickly. However,
+ // when it is not going down, we want to be careful not to release
+ // too much back to the OS, as it could cause additional paging.
+
+ // We use a damping function to lessen the working set over time.
+ // As the process grows/shrinks, this algorithm will lag with
+ // working set reduction.
+ //
+ // The intended algorithm is:
+ // TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2
+
+ scoped_ptr<process_util::ProcessMetrics> metrics(
+ process_util::ProcessMetrics::CreateProcessMetrics(process_));
+ process_util::WorkingSetKBytes working_set;
+ if (!metrics->GetWorkingSetKBytes(&working_set))
+ return false;
+
+
+ // We want to compute the amount of working set that the process
+ // needs to keep in memory. Since other processes contain the
+ // pages which are shared, we don't need to reserve them in our
+ // process, the system already has them tagged. Keep in mind, we
+ // don't get to control *which* pages get released, but if we
+ // assume reasonable distribution of pages, this should generally
+ // be the right value.
+ size_t current_working_set_size = working_set.priv +
+ working_set.shareable;
+
+ size_t max_size = current_working_set_size;
+ if (last_working_set_size_)
+ max_size = (max_size + last_working_set_size_) / 2; // Average.
+ max_size *= 1024; // Convert to KBytes.
+ last_working_set_size_ = current_working_set_size / kDampingFactor;
+
+ BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, max_size);
+ return rv == TRUE;
+}
+
+bool Process::UnReduceWorkingSet() {
+ if (!process_)
+ return false;
+
+ if (!last_working_set_size_)
+ return true; // There was nothing to undo.
+
+ // We've had a reduced working set. Make sure we have lots of
+ // headroom now that we're active again.
+ size_t limit = last_working_set_size_ * kDampingFactor * 2 * 1024;
+ BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, limit);
+ return rv == TRUE;
+}
+
+bool Process::EmptyWorkingSet() {
+ if (!process_)
+ return false;
+
+ BOOL rv = SetProcessWorkingSetSize(process_, -1, -1);
+ return rv == TRUE;
+}
+
+int32 Process::pid() const {
+ if (process_ == 0)
+ return 0;
+
+ return process_util::GetProcId(process_);
+}
+
+bool Process::is_current() const {
+ return process_ == GetCurrentProcess();
+}
+
+// static
+Process Process::Current() {
+ return Process(GetCurrentProcess());
+}
diff --git a/base/process.h b/base/process.h
new file mode 100644
index 0000000..b8ea94f
--- /dev/null
+++ b/base/process.h
@@ -0,0 +1,106 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_PROCESS_H__
+#define BASE_PROCESS_H__
+
+#include <windows.h>
+#include "base/basictypes.h"
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+#ifdef WIN32
+typedef HANDLE ProcessHandle;
+#else
+typedef int ProcessHandle;
+#endif
+
+// A Process
+// TODO(mbelshe): Replace existing code which uses the ProcessHandle w/ the
+// Process object where relevant.
+class Process {
+ public:
+ Process() : process_(0), last_working_set_size_(0) {}
+ explicit Process(ProcessHandle handle) :
+ process_(handle), last_working_set_size_(0) {}
+
+ // A handle to the current process.
+ static Process Current();
+
+ // Get/Set the handle for this process.
+ ProcessHandle handle() { return process_; }
+ void set_handle(ProcessHandle handle) { process_ = handle; }
+
+ // Get the PID for this process.
+ int32 pid() const;
+
+ // Is the this process the current process.
+ bool is_current() const;
+
+ // Close the Process Handle.
+ void Close() {
+ CloseHandle(process_);
+ process_ = 0;
+ }
+
+ // A process is backgrounded when it's priority is lower than normal.
+ // Return true if this process is backgrounded, false otherwise.
+ bool IsProcessBackgrounded();
+
+ // Set a prcess as backgrounded. If value is true, the priority
+ // of the process will be lowered. If value is false, the priority
+ // of the process will be made "normal" - equivalent to default
+ // process priority.
+ // Returns true if the priority was changed, false otherwise.
+ bool SetProcessBackgrounded(bool value);
+
+ // Reduces the working set of memory used by the process.
+ // The algorithm used by this function is intentionally vague. Repeated calls
+ // to this function consider the process' previous required Working Set sizes
+ // to determine a reasonable reduction. This helps give memory back to the OS
+ // in increments without over releasing memory.
+ // When the WorkingSet is reduced, it is permanent, until the caller calls
+ // UnReduceWorkingSet.
+ // Returns true if successful, false otherwise.
+ bool ReduceWorkingSet();
+
+ // Undoes the effects of prior calls to ReduceWorkingSet().
+ // Returns true if successful, false otherwise.
+ bool UnReduceWorkingSet();
+
+ // Releases as much of the working set back to the OS as possible.
+ // Returns true if successful, false otherwise.
+ bool EmptyWorkingSet();
+
+ private:
+ ProcessHandle process_;
+ size_t last_working_set_size_;
+};
+
+#endif // BASE_PROCESS_H__
diff --git a/base/process_util.cc b/base/process_util.cc
new file mode 100644
index 0000000..09631ac
--- /dev/null
+++ b/base/process_util.cc
@@ -0,0 +1,581 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/process_util.h"
+
+#include <windows.h>
+#include <winternl.h>
+#include <psapi.h>
+
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+
+namespace {
+
+// System pagesize. This value remains constant on x86/64 architectures.
+const int PAGESIZE_KB = 4;
+
+// HeapSetInformation function pointer.
+typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T);
+
+} // namespace
+
+namespace process_util {
+
+int GetCurrentProcId() {
+ return ::GetCurrentProcessId();
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to GetProcessId().
+ typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE);
+ static GetProcessIdFunction GetProcessIdPtr = NULL;
+ static bool initialize_get_process_id = true;
+ if (initialize_get_process_id) {
+ initialize_get_process_id = false;
+ HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll");
+ if (!kernel32_handle) {
+ NOTREACHED();
+ return false;
+ }
+ GetProcessIdPtr = reinterpret_cast<GetProcessIdFunction>(GetProcAddress(
+ kernel32_handle, "GetProcessId"));
+ }
+ if (!GetProcessIdPtr)
+ return false;
+ // Ask for the process ID.
+ *id = (*GetProcessIdPtr)(process);
+ return true;
+}
+
+// Helper for GetProcId()
+bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) {
+ // Dynamically get a pointer to NtQueryInformationProcess().
+ typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)(
+ HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
+ static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL;
+ static bool initialize_query_information_process = true;
+ if (initialize_query_information_process) {
+ initialize_query_information_process = false;
+ // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though
+ // the Windows docs seem to imply that you should LoadLibrary() it.
+ HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll");
+ if (!ntdll_handle) {
+ NOTREACHED();
+ return false;
+ }
+ NtQueryInformationProcessPtr =
+ reinterpret_cast<NtQueryInformationProcessFunction>(GetProcAddress(
+ ntdll_handle, "NtQueryInformationProcess"));
+ }
+ if (!NtQueryInformationProcessPtr)
+ return false;
+ // Ask for the process ID.
+ PROCESS_BASIC_INFORMATION info;
+ ULONG bytes_returned;
+ NTSTATUS status = (*NtQueryInformationProcessPtr)(process,
+ ProcessBasicInformation,
+ &info, sizeof info,
+ &bytes_returned);
+ if (!SUCCEEDED(status) || (bytes_returned != (sizeof info)))
+ return false;
+
+ *id = static_cast<DWORD>(info.UniqueProcessId);
+ return true;
+}
+
+int GetProcId(ProcessHandle process) {
+ // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights.
+ HANDLE current_process = GetCurrentProcess();
+ HANDLE process_with_query_rights;
+ if (DuplicateHandle(current_process, process, current_process,
+ &process_with_query_rights, PROCESS_QUERY_INFORMATION,
+ false, 0)) {
+ // Try to use GetProcessId(), if it exists. Fall back on
+ // NtQueryInformationProcess() otherwise (< Win XP SP1).
+ DWORD id;
+ bool success =
+ GetProcIdViaGetProcessId(process_with_query_rights, &id) ||
+ GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id);
+ CloseHandle(process_with_query_rights);
+ if (success)
+ return id;
+ }
+
+ // We're screwed.
+ NOTREACHED();
+ return 0;
+}
+
+bool LaunchApp(const std::wstring& cmdline,
+ bool wait, bool start_hidden, ProcessHandle* process_handle) {
+ STARTUPINFO startup_info = {0};
+ startup_info.cb = sizeof(startup_info);
+ if (start_hidden) {
+ startup_info.dwFlags = STARTF_USESHOWWINDOW;
+ startup_info.wShowWindow = SW_HIDE;
+ }
+ PROCESS_INFORMATION process_info;
+ if (!CreateProcess(NULL,
+ const_cast<wchar_t*>(cmdline.c_str()), NULL, NULL,
+ FALSE, 0, NULL, NULL,
+ &startup_info, &process_info))
+ return false;
+
+ // Handles must be closed or they will leak
+ CloseHandle(process_info.hThread);
+
+ if (wait)
+ WaitForSingleObject(process_info.hProcess, INFINITE);
+
+ // If the caller wants the process handle, we won't close it.
+ if (process_handle) {
+ *process_handle = process_info.hProcess;
+ } else {
+ CloseHandle(process_info.hProcess);
+ }
+ return true;
+}
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(int process_id, int exit_code, bool wait) {
+ bool result = false;
+ HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE,
+ FALSE, // Don't inherit handle
+ process_id);
+ if (process) {
+ result = !!TerminateProcess(process, exit_code);
+ if (result && wait) {
+ // The process may not end immediately due to pending I/O
+ if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000))
+ DLOG(ERROR) << "Error waiting for process exit: " << GetLastError();
+ } else {
+ DLOG(ERROR) << "Unable to terminate process: " << GetLastError();
+ }
+ CloseHandle(process);
+ }
+ return result;
+}
+
+bool DidProcessCrash(ProcessHandle handle) {
+ DWORD exitcode = 0;
+ BOOL success = ::GetExitCodeProcess(handle, &exitcode);
+ DCHECK(success);
+ DCHECK(exitcode != STILL_ACTIVE);
+
+ if (exitcode == 0 || // Normal termination.
+ exitcode == 1 || // Killed by task manager.
+ exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE
+ exitcode == 0xC000013A || // Control-C/end session.
+ exitcode == 0x40010004) { // Debugger terminated process/end session.
+ return false;
+ }
+
+ // All other exit codes indicate crashes.
+ return true;
+}
+
+NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name,
+ const ProcessFilter* filter) :
+ started_iteration_(false),
+ executable_name_(executable_name),
+ filter_(filter) {
+ snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ }
+
+NamedProcessIterator::~NamedProcessIterator() {
+ CloseHandle(snapshot_);
+}
+
+
+const ProcessEntry* NamedProcessIterator::NextProcessEntry() {
+ bool result = false;
+ do {
+ result = CheckForNextProcess();
+ } while (result && !IncludeEntry());
+
+ if (result) {
+ return &entry_;
+ }
+
+ return NULL;
+}
+
+bool NamedProcessIterator::CheckForNextProcess() {
+ InitProcessEntry(&entry_);
+
+ if (!started_iteration_) {
+ started_iteration_ = true;
+ return !!Process32First(snapshot_, &entry_);
+ }
+
+ return !!Process32Next(snapshot_, &entry_);
+}
+
+bool NamedProcessIterator::IncludeEntry() {
+ return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 &&
+ (!filter_ || filter_->Includes(entry_.th32ProcessID,
+ entry_.th32ParentProcessID));
+}
+
+void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) {
+ memset(entry, 0, sizeof(*entry));
+ entry->dwSize = sizeof(*entry);
+}
+
+int GetProcessCount(const std::wstring& executable_name,
+ const ProcessFilter* filter) {
+ int count = 0;
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (iter.NextProcessEntry())
+ ++count;
+ return count;
+}
+
+bool KillProcesses(const std::wstring& executable_name, int exit_code,
+ const ProcessFilter* filter) {
+ bool result = true;
+ const ProcessEntry* entry;
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (entry = iter.NextProcessEntry())
+ result = KillProcess((*entry).th32ProcessID, exit_code, true) && result;
+
+ return result;
+}
+
+bool WaitForProcessesToExit(const std::wstring& executable_name,
+ int wait_milliseconds,
+ const ProcessFilter* filter) {
+ const ProcessEntry* entry;
+ bool result = true;
+ DWORD start_time = GetTickCount();
+
+ NamedProcessIterator iter(executable_name, filter);
+ while (entry = iter.NextProcessEntry()) {
+ DWORD remaining_wait =
+ std::max(0, wait_milliseconds -
+ static_cast<int>(GetTickCount() - start_time));
+ HANDLE process = OpenProcess(SYNCHRONIZE,
+ FALSE,
+ entry->th32ProcessID);
+ DWORD wait_result = WaitForSingleObject(process, remaining_wait);
+ CloseHandle(process);
+ result = result && (wait_result == WAIT_OBJECT_0);
+ }
+
+ return result;
+}
+
+bool CleanupProcesses(const std::wstring& executable_name,
+ int wait_milliseconds,
+ int exit_code,
+ const ProcessFilter* filter) {
+ bool exited_cleanly =
+ process_util::WaitForProcessesToExit(executable_name, wait_milliseconds,
+ filter);
+ if (!exited_cleanly)
+ process_util::KillProcesses(executable_name, exit_code, filter);
+ return exited_cleanly;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// ProcesMetrics
+
+ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process),
+ last_time_(0),
+ last_system_time_(0) {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ processor_count_ = system_info.dwNumberOfProcessors;
+}
+
+// static
+ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) {
+ return new ProcessMetrics(process);
+}
+
+ProcessMetrics::~ProcessMetrics() { }
+
+size_t ProcessMetrics::GetPagefileUsage() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the peak space allocated for the pagefile, in bytes.
+size_t ProcessMetrics::GetPeakPagefileUsage() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.PeakPagefileUsage;
+ }
+ return 0;
+}
+
+// Returns the current working set size, in bytes.
+size_t ProcessMetrics::GetWorkingSetSize() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) {
+ return pmc.WorkingSetSize;
+ }
+ return 0;
+}
+
+size_t ProcessMetrics::GetPrivateBytes() {
+ // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2.
+ // GetProcessMemoryInfo() will simply fail on prior OS. So the requested
+ // information is simply not available. Hence, we will return 0 on unsupported
+ // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member.
+ PROCESS_MEMORY_COUNTERS_EX pmcx;
+ if (GetProcessMemoryInfo(process_,
+ reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmcx),
+ sizeof(pmcx))) {
+ return pmcx.PrivateUsage;
+ }
+ return 0;
+}
+
+void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) {
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ size_t committed_private = 0;
+ size_t committed_mapped = 0;
+ size_t committed_image = 0;
+ void* base_address = NULL;
+ while (VirtualQueryEx(process_, base_address, &mbi,
+ sizeof(MEMORY_BASIC_INFORMATION)) ==
+ sizeof(MEMORY_BASIC_INFORMATION)) {
+ if(mbi.State == MEM_COMMIT) {
+ if (mbi.Type == MEM_PRIVATE) {
+ committed_private += mbi.RegionSize;
+ } else if (mbi.Type == MEM_MAPPED) {
+ committed_mapped += mbi.RegionSize;
+ } else if (mbi.Type == MEM_IMAGE) {
+ committed_image += mbi.RegionSize;
+ } else {
+ NOTREACHED();
+ }
+ }
+ base_address = (static_cast<BYTE*>(mbi.BaseAddress)) + mbi.RegionSize;
+ }
+ usage->image = committed_image / 1024;
+ usage->mapped = committed_mapped / 1024;
+ usage->priv = committed_private / 1024;
+}
+
+bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) {
+ size_t ws_private = 0;
+ size_t ws_shareable = 0;
+ size_t ws_shared = 0;
+
+ DCHECK(ws_usage);
+ memset(ws_usage, 0, sizeof(*ws_usage));
+
+ DWORD number_of_entries = 4096; // Just a guess.
+ PSAPI_WORKING_SET_INFORMATION* buffer = NULL;
+ int retries = 5;
+ for(;;) {
+ DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) +
+ (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK));
+
+ // if we can't expand the buffer, don't leak the previous
+ // contents or pass a NULL pointer to QueryWorkingSet
+ PSAPI_WORKING_SET_INFORMATION* new_buffer = reinterpret_cast<PSAPI_WORKING_SET_INFORMATION*>(
+ realloc(buffer, buffer_size));
+ if (!new_buffer) {
+ free(buffer);
+ return false;
+ }
+ buffer = new_buffer;
+
+ // Call the function once to get number of items
+ if (QueryWorkingSet(process_, buffer, buffer_size))
+ break; // Success
+
+ if (GetLastError() != ERROR_BAD_LENGTH) {
+ free(buffer);
+ return false;
+ }
+
+ number_of_entries = static_cast<DWORD>(buffer->NumberOfEntries);
+
+ // Maybe some entries are being added right now. Increase the buffer to
+ // take that into account.
+ number_of_entries = static_cast<DWORD>(number_of_entries * 1.25);
+
+ if (--retries == 0) {
+ free(buffer); // If we're looping, eventually fail.
+ return false;
+ }
+ }
+
+ // On windows 2000 the function returns 1 even when the buffer is too small.
+ // The number of entries that we are going to parse is the minimum between the
+ // size we allocated and the real number of entries.
+ number_of_entries =
+ std::min(number_of_entries, static_cast<DWORD>(buffer->NumberOfEntries));
+ for (unsigned int i = 0; i < number_of_entries; i++) {
+ if (buffer->WorkingSetInfo[i].Shared) {
+ ws_shareable++;
+ if (buffer->WorkingSetInfo[i].ShareCount > 1)
+ ws_shared++;
+ } else {
+ ws_private++;
+ }
+ }
+
+ ws_usage->priv = ws_private * PAGESIZE_KB;
+ ws_usage->shareable = ws_shareable * PAGESIZE_KB;
+ ws_usage->shared = ws_shared * PAGESIZE_KB;
+ free(buffer);
+ return true;
+}
+
+static uint64 FileTimeToUTC(const FILETIME& ftime) {
+ LARGE_INTEGER li;
+ li.LowPart = ftime.dwLowDateTime;
+ li.HighPart = ftime.dwHighDateTime;
+ return li.QuadPart;
+}
+
+int ProcessMetrics::GetCPUUsage() {
+ FILETIME now;
+ FILETIME creation_time;
+ FILETIME exit_time;
+ FILETIME kernel_time;
+ FILETIME user_time;
+
+ GetSystemTimeAsFileTime(&now);
+
+ if (!GetProcessTimes(process_, &creation_time, &exit_time,
+ &kernel_time, &user_time)) {
+ // We don't assert here because in some cases (such as in the Task Manager)
+ // we may call this function on a process that has just exited but we have
+ // not yet received the notification.
+ return 0;
+ }
+ int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) /
+ processor_count_;
+ int64 time = FileTimeToUTC(now);
+
+ if ((last_system_time_ == 0) || (last_time_ == 0)) {
+ // First call, just set the last values.
+ last_system_time_ = system_time;
+ last_time_ = time;
+ return 0;
+ }
+
+ int64 system_time_delta = system_time - last_system_time_;
+ int64 time_delta = time - last_time_;
+ DCHECK(time_delta != 0);
+ if (time_delta == 0)
+ return 0;
+
+ // We add time_delta / 2 so the result is rounded.
+ int cpu = static_cast<int>((system_time_delta * 100 + time_delta / 2) /
+ time_delta);
+
+ last_system_time_ = system_time;
+ last_time_ = time;
+
+ return cpu;
+}
+
+bool ProcessMetrics::GetIOCounters(IO_COUNTERS* io_counters) {
+ return GetProcessIoCounters(process_, io_counters) != FALSE;
+}
+
+bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) {
+ const SIZE_T kTopAdress = 0x7F000000;
+ const SIZE_T kMegabyte = 1024 * 1024;
+ SIZE_T accumulated = 0;
+
+ MEMORY_BASIC_INFORMATION largest = {0};
+ UINT_PTR scan = 0;
+ while (scan < kTopAdress) {
+ MEMORY_BASIC_INFORMATION info;
+ if (!::VirtualQueryEx(process_, reinterpret_cast<void*>(scan),
+ &info, sizeof(info)))
+ return false;
+ if (info.State == MEM_FREE) {
+ accumulated += info.RegionSize;
+ UINT_PTR end = scan + info.RegionSize;
+ if (info.RegionSize > (largest.RegionSize))
+ largest = info;
+ }
+ scan += info.RegionSize;
+ }
+ free->largest = largest.RegionSize / kMegabyte;
+ free->largest_ptr = largest.BaseAddress;
+ free->total = accumulated / kMegabyte;
+ return true;
+}
+
+bool EnableLowFragmentationHeap() {
+ HMODULE kernel32 = GetModuleHandle(L"kernel32.dll");
+ HeapSetFn heap_set = reinterpret_cast<HeapSetFn>(GetProcAddress(
+ kernel32,
+ "HeapSetInformation"));
+
+ // On Windows 2000, the function is not exported. This is not a reason to
+ // fail.
+ if (!heap_set)
+ return true;
+
+ unsigned number_heaps = GetProcessHeaps(0, NULL);
+ if (!number_heaps)
+ return false;
+
+ // Gives us some extra space in the array in case a thread is creating heaps
+ // at the same time we're querying them.
+ static const int MARGIN = 8;
+ scoped_array<HANDLE> heaps(new HANDLE[number_heaps + MARGIN]);
+ number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get());
+ if (!number_heaps)
+ return false;
+
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG lfh_flag = 2;
+ // Don't bother with the result code. It may fails on heaps that have the
+ // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all.
+ heap_set(heaps[i],
+ HeapCompatibilityInformation,
+ &lfh_flag,
+ sizeof(lfh_flag));
+ }
+ return true;
+}
+
+} // namespace process_util
diff --git a/base/process_util.h b/base/process_util.h
new file mode 100644
index 0000000..1bdd9e9
--- /dev/null
+++ b/base/process_util.h
@@ -0,0 +1,285 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file/namespace contains utility functions for enumerating, ending and
+// computing statistics of processes.
+
+#ifndef BASE_PROCESS_UTIL_H__
+#define BASE_PROCESS_UTIL_H__
+
+#include <string>
+#ifdef WIN32
+#include <windows.h>
+#include <tlhelp32.h>
+#endif // WIN32
+
+#include "base/basictypes.h"
+#include "base/process.h"
+
+// ProcessHandle is a platform specific type which represents the underlying OS
+// handle to a process.
+#ifdef WIN32
+typedef PROCESSENTRY32 ProcessEntry;
+typedef IO_COUNTERS IoCounters;
+#else
+typedef int ProcessEntry;
+typedef int IoCounters; //TODO(awalker): replace with struct when available
+#endif
+
+namespace process_util {
+
+// Returns the id of the current process.
+int GetCurrentProcId();
+
+// Returns the unique ID for the specified process. This is functionally the
+// same as Windows' GetProcessId(), but works on versions of Windows before
+// Win XP SP1 as well.
+int GetProcId(ProcessHandle process);
+
+// Runs the given application name with the given command line. Normally, the
+// first command line argument should be the path to the process, and don't
+// forget to quote it.
+//
+// If wait is true, it will block and wait for the other process to finish,
+// otherwise, it will just continue asynchronously.
+//
+// Example (including literal quotes)
+// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\"
+//
+// If process_handle is non-NULL, the process handle of the launched app will be
+// stored there on a successful launch.
+// NOTE: In this case, the caller is responsible for closing the handle so
+// that it doesn't leak!
+bool LaunchApp(const std::wstring& cmdline,
+ bool wait, bool start_hidden, ProcessHandle* process_handle);
+
+// Used to filter processes by process ID.
+class ProcessFilter {
+ public:
+ // Returns true to indicate set-inclusion and false otherwise. This method
+ // should not have side-effects and should be idempotent.
+ virtual bool Includes(uint32 pid, uint32 parent_pid) const = 0;
+};
+
+// Returns the number of processes on the machine that are running from the
+// given executable name. If filter is non-null, then only processes selected
+// by the filter will be counted.
+int GetProcessCount(const std::wstring& executable_name,
+ const ProcessFilter* filter);
+
+// Attempts to kill all the processes on the current machine that were launched
+// from the given executable name, ending them with the given exit code. If
+// filter is non-null, then only processes selected by the filter are killed.
+// Returns false if all processes were able to be killed off, false if at least
+// one couldn't be killed.
+bool KillProcesses(const std::wstring& executable_name, int exit_code,
+ const ProcessFilter* filter);
+
+// Attempts to kill the process identified by the given process
+// entry structure, giving it the specified exit code. If |wait| is true, wait
+// for the process to be actually terminated before returning.
+// Returns true if this is successful, false otherwise.
+bool KillProcess(int process_id, int exit_code, bool wait);
+
+// Get the termination status (exit code) of the process and return true if the
+// status indicates the process crashed. It is an error to call this if the
+// process hasn't terminated yet.
+bool DidProcessCrash(ProcessHandle handle);
+
+// Wait for all the processes based on the named executable to exit. If filter
+// is non-null, then only processes selected by the filter are waited on.
+// Returns after all processes have exited or wait_milliseconds have expired.
+// Returns true if all the processes exited, false otherwise.
+bool WaitForProcessesToExit(const std::wstring& executable_name,
+ int wait_milliseconds,
+ const ProcessFilter* filter);
+
+// Waits a certain amount of time (can be 0) for all the processes with a given
+// executable name to exit, then kills off any of them that are still around.
+// If filter is non-null, then only processes selected by the filter are waited
+// on. Killed processes are ended with the given exit code. Returns false if
+// any processes needed to be killed, true if they all exited cleanly within
+// the wait_milliseconds delay.
+bool CleanupProcesses(const std::wstring& executable_name,
+ int wait_milliseconds,
+ int exit_code,
+ const ProcessFilter* filter);
+
+// This class provides a way to iterate through the list of processes
+// on the current machine that were started from the given executable
+// name. To use, create an instance and then call NextProcessEntry()
+// until it returns false.
+class NamedProcessIterator {
+ public:
+ NamedProcessIterator(const std::wstring& executable_name,
+ const ProcessFilter* filter);
+ ~NamedProcessIterator();
+
+ // If there's another process that matches the given executable name,
+ // returns a const pointer to the corresponding PROCESSENTRY32.
+ // If there are no more matching processes, returns NULL.
+ // The returned pointer will remain valid until NextProcessEntry()
+ // is called again or this NamedProcessIterator goes out of scope.
+ const ProcessEntry* NextProcessEntry();
+
+ private:
+ // Determines whether there's another process (regardless of executable)
+ // left in the list of all processes. Returns true and sets entry_ to
+ // that process's info if there is one, false otherwise.
+ bool CheckForNextProcess();
+
+ bool IncludeEntry();
+
+ // Initializes a PROCESSENTRY32 data structure so that it's ready for
+ // use with Process32First/Process32Next.
+ void InitProcessEntry(ProcessEntry* entry);
+
+ std::wstring executable_name_;
+#ifdef WIN32
+ HANDLE snapshot_;
+#endif WIN32
+ bool started_iteration_;
+ ProcessEntry entry_;
+ const ProcessFilter* filter_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(NamedProcessIterator);
+};
+
+// Working Set (resident) memory usage broken down by
+// priv (private): These pages (kbytes) cannot be shared with any other process.
+// shareable: These pages (kbytes) can be shared with other processes under
+// the right circumstances.
+// shared : These pages (kbytes) are currently shared with at least one
+// other process.
+struct WorkingSetKBytes {
+ size_t priv;
+ size_t shareable;
+ size_t shared;
+};
+
+// Committed (resident + paged) memory usage broken down by
+// private: These pages cannot be shared with any other process.
+// mapped: These pages are mapped into the view of a section (backed by
+// pagefile.sys)
+// image: These pages are mapped into the view of an image section (backed by
+// file system)
+struct CommittedKBytes {
+ size_t priv;
+ size_t mapped;
+ size_t image;
+};
+
+// Free memory (Megabytes marked as free) in the 2G process address space.
+// total : total amount in megabytes marked as free. Maximum value is 2048.
+// largest : size of the largest contiguous amount of memory found. It is
+// always smaller or equal to FreeMBytes::total.
+// largest_ptr: starting address of the largest memory block.
+struct FreeMBytes {
+ size_t total;
+ size_t largest;
+ void* largest_ptr;
+};
+
+// Provides performance metrics for a specified process (CPU usage, memory and
+// IO counters). To use it, invoke CreateProcessMetrics() to get an instance
+// for a specific process, then access the information with the different get
+// methods.
+class ProcessMetrics {
+ public:
+ // Creates a ProcessMetrics for the specified process.
+ // The caller owns the returned object.
+ static ProcessMetrics* CreateProcessMetrics(ProcessHandle process);
+
+ ~ProcessMetrics();
+
+ // Returns the current space allocated for the pagefile, in bytes (these pages
+ // may or may not be in memory).
+ size_t GetPagefileUsage();
+ // Returns the peak space allocated for the pagefile, in bytes.
+ size_t GetPeakPagefileUsage();
+ // Returns the current working set size, in bytes.
+ size_t GetWorkingSetSize();
+ // Returns private usage, in bytes. Private bytes is the amount
+ // of memory currently allocated to a process that cannot be shared.
+ // Note: returns 0 on unsupported OSes: prior to XP SP2.
+ size_t GetPrivateBytes();
+ // Fills a CommittedKBytes with both resident and paged
+ // memory usage as per definition of CommittedBytes.
+ void GetCommittedKBytes(CommittedKBytes* usage);
+ // Fills a WorkingSetKBytes containing resident private and shared memory
+ // usage in bytes, as per definition of WorkingSetBytes.
+ bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage);
+
+ // Computes the current process available memory for allocation.
+ // It does a linear scan of the address space querying each memory region
+ // for its free (unallocated) status. It is useful for estimating the memory
+ // load and fragmentation.
+ bool CalculateFreeMemory(FreeMBytes* free);
+
+ // 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.
+ // Note that on multi-processor machines, 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.
+ int GetCPUUsage();
+
+ // Retrieves accounting information for all I/O operations performed by the
+ // process.
+ // If IO information is retrieved successfully, the function returns true
+ // and fills in the IO_COUNTERS passed in. The function returns false
+ // otherwise.
+ bool GetIOCounters(IoCounters* io_counters);
+
+ private:
+ explicit ProcessMetrics(ProcessHandle process);
+
+ ProcessHandle process_;
+
+ int processor_count_;
+
+ // Used to store the previous times so we can compute the CPU usage.
+ int64 last_time_;
+ int64 last_system_time_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessMetrics);
+};
+
+// Enables low fragmentation heap (LFH) for every heaps of this process. This
+// won't have any effect on heaps created after this function call. It will not
+// modify data allocated in the heaps before calling this function. So it is
+// better to call this function early in initialization and again before
+// entering the main loop.
+// Note: Returns true on Windows 2000 without doing anything.
+bool EnableLowFragmentationHeap();
+
+} // namespace process_util
+
+
+#endif // BASE_PROCESS_UTIL_H__
diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc
new file mode 100644
index 0000000..d8bfb33
--- /dev/null
+++ b/base/process_util_unittest.cc
@@ -0,0 +1,99 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/process_util.h"
+
+TEST(ProcessUtilTest, EnableLFH) {
+ ASSERT_TRUE(process_util::EnableLowFragmentationHeap());
+ if (IsDebuggerPresent()) {
+ // Under these conditions, LFH can't be enabled. There's no point to test
+ // anything.
+ const char* no_debug_env = getenv("_NO_DEBUG_HEAP");
+ if (!no_debug_env || strcmp(no_debug_env, "1"))
+ return;
+ }
+ HANDLE heaps[1024] = { 0 };
+ unsigned number_heaps = GetProcessHeaps(1024, heaps);
+ EXPECT_GT(number_heaps, 0u);
+ for (unsigned i = 0; i < number_heaps; ++i) {
+ ULONG flag = 0;
+ SIZE_T length;
+ ASSERT_NE(0, HeapQueryInformation(heaps[i],
+ HeapCompatibilityInformation,
+ &flag,
+ sizeof(flag),
+ &length));
+ // If flag is 0, the heap is a standard heap that does not support
+ // look-asides. If flag is 1, the heap supports look-asides. If flag is 2,
+ // the heap is a low-fragmentation heap (LFH). Note that look-asides are not
+ // supported on the LFH.
+
+ // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag.
+ EXPECT_LE(flag, 2u);
+ EXPECT_NE(flag, 1u);
+ }
+}
+
+TEST(ProcessUtilTest, CalcFreeMemory) {
+ process_util::ProcessMetrics* metrics =
+ process_util::ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess());
+ ASSERT_TRUE(NULL != metrics);
+
+ // Typical values here is ~1900 for total and ~1000 for largest. Obviously
+ // it depends in what other tests have done to this process.
+ process_util::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;
+ char* alloc = new char[kAllocMB * 1024 * 1024];
+ EXPECT_TRUE(NULL != alloc);
+
+ size_t expected_total = free_mem1.total - kAllocMB;
+ size_t expected_largest = free_mem1.largest;
+
+ process_util::FreeMBytes free_mem2 = {0};
+ EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2));
+ EXPECT_GE(free_mem2.total, free_mem2.largest);
+ EXPECT_GE(expected_total, free_mem2.total);
+ EXPECT_GE(expected_largest, free_mem2.largest);
+ EXPECT_TRUE(NULL != free_mem2.largest_ptr);
+
+ delete[] alloc;
+ delete metrics;
+}
diff --git a/base/ref_counted.h b/base/ref_counted.h
new file mode 100644
index 0000000..36f4ab4
--- /dev/null
+++ b/base/ref_counted.h
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_REF_COUNTED_H__
+#define BASE_REF_COUNTED_H__
+
+#include "base/atomic.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace base {
+
+//
+// A base class for reference counted classes. Otherwise, known as a cheap
+// knock-off of WebKit's RefCounted<T> class. To use this guy just extend your
+// class from it like so:
+//
+// class MyFoo : public base::RefCounted<MyFoo> {
+// ...
+// };
+//
+template <class T>
+class RefCounted {
+ public:
+ RefCounted() : ref_count_(0) {
+#ifndef NDEBUG
+ in_dtor_ = false;
+#endif
+ }
+
+ ~RefCounted() {
+#ifndef NDEBUG
+ DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()";
+#endif
+ }
+
+ void AddRef() {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ ++ref_count_;
+ }
+
+ void Release() {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ if (--ref_count_ == 0) {
+#ifndef NDEBUG
+ in_dtor_ = true;
+#endif
+ delete static_cast<T*>(this);
+ }
+ }
+
+ private:
+ int ref_count_;
+#ifndef NDEBUG
+ bool in_dtor_;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(RefCounted<T>);
+};
+
+//
+// A thread-safe variant of RefCounted<T>
+//
+// class MyFoo : public base::RefCountedThreadSafe<MyFoo> {
+// ...
+// };
+//
+template <class T>
+class RefCountedThreadSafe {
+ public:
+ RefCountedThreadSafe() : ref_count_(0) {
+#ifndef NDEBUG
+ in_dtor_ = false;
+#endif
+ }
+
+ ~RefCountedThreadSafe() {
+#ifndef NDEBUG
+ DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " <<
+ "calling Release()";
+#endif
+ }
+
+ void AddRef() {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ AtomicIncrement(&ref_count_);
+ }
+
+ void Release() {
+#ifndef NDEBUG
+ DCHECK(!in_dtor_);
+#endif
+ // We need to insert memory barriers to ensure that state written before
+ // the reference count became 0 will be visible to a thread that has just
+ // made the count 0.
+ // TODO(wtc): Bug 1112286: use the barrier variant of AtomicDecrement.
+ if (AtomicDecrement(&ref_count_) == 0) {
+#ifndef NDEBUG
+ in_dtor_ = true;
+#endif
+ delete static_cast<T*>(this);
+ }
+ }
+
+ private:
+ int32 ref_count_;
+#ifndef NDEBUG
+ bool in_dtor_;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(RefCountedThreadSafe<T>);
+};
+
+} // namespace base
+
+//
+// A smart pointer class for reference counted objects. Use this class instead
+// of calling AddRef and Release manually on a reference counted object to
+// avoid common memory leaks caused by forgetting to Release an object
+// reference. Sample usage:
+//
+// class MyFoo : public RefCounted<MyFoo> {
+// ...
+// };
+//
+// void some_function() {
+// scoped_refptr<MyFoo> foo = new MyFoo();
+// foo->Method(param);
+// // |foo| is released when this function returns
+// }
+//
+// void some_other_function() {
+// scoped_refptr<MyFoo> foo = new MyFoo();
+// ...
+// foo = NULL; // explicitly releases |foo|
+// ...
+// if (foo)
+// foo->Method(param);
+// }
+//
+// The above examples show how scoped_refptr<T> acts like a pointer to T.
+// Given two scoped_refptr<T> classes, it is also possible to exchange
+// references between the two objects, like so:
+//
+// {
+// scoped_refptr<MyFoo> a = new MyFoo();
+// scoped_refptr<MyFoo> b;
+//
+// b.swap(a);
+// // now, |b| references the MyFoo object, and |a| references NULL.
+// }
+//
+// To make both |a| and |b| in the above example reference the same MyFoo
+// object, simply use the assignment operator:
+//
+// {
+// scoped_refptr<MyFoo> a = new MyFoo();
+// scoped_refptr<MyFoo> b;
+//
+// b = a;
+// // now, |a| and |b| each own a reference to the same MyFoo object.
+// }
+//
+template <class T>
+class scoped_refptr {
+ public:
+ scoped_refptr() : ptr_(NULL) {
+ }
+
+ scoped_refptr(T* p) : ptr_(p) {
+ if (ptr_)
+ ptr_->AddRef();
+ }
+
+ scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
+ if (ptr_)
+ ptr_->AddRef();
+ }
+
+ ~scoped_refptr() {
+ if (ptr_)
+ ptr_->Release();
+ }
+
+ T* get() const { return ptr_; }
+ operator T*() const { return ptr_; }
+ T* operator->() const { return ptr_; }
+
+ scoped_refptr<T>& operator=(T* p) {
+ // AddRef first so that self assignment should work
+ if (p)
+ p->AddRef();
+ if (ptr_ )
+ ptr_ ->Release();
+ ptr_ = p;
+ return *this;
+ }
+
+ scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
+ return *this = r.ptr_;
+ }
+
+ void swap(T** pp) {
+ T* p = ptr_;
+ ptr_ = *pp;
+ *pp = p;
+ }
+
+ void swap(scoped_refptr<T>& r) {
+ swap(&r.ptr_);
+ }
+
+ private:
+ T* ptr_;
+};
+
+#endif // BASE_REF_COUNTED_H__
diff --git a/base/ref_counted_unittest.cc b/base/ref_counted_unittest.cc
new file mode 100644
index 0000000..ec0186c
--- /dev/null
+++ b/base/ref_counted_unittest.cc
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/ref_counted.h"
+
+class SelfAssign : public base::RefCounted<SelfAssign> {
+};
+
+TEST(RefCountedUnitTest, TestSelfAssignment) {
+ SelfAssign* p = new SelfAssign;
+ scoped_refptr<SelfAssign> var = p;
+ var = var;
+ EXPECT_EQ(var.get(), p);
+}
diff --git a/base/registry.cc b/base/registry.cc
new file mode 100644
index 0000000..642973f
--- /dev/null
+++ b/base/registry.cc
@@ -0,0 +1,481 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#include <assert.h>
+#include <shlwapi.h>
+#include <windows.h>
+
+#include "base/registry.h"
+
+#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey
+
+// local types (see the same declarations in the header file)
+#define tchar TCHAR
+#define CTP const tchar*
+#define tstr std::basic_string<tchar>
+
+//
+// RegistryValueIterator
+//
+
+
+RegistryValueIterator::RegistryValueIterator(HKEY root_key,
+ LPCTSTR folder_key) {
+ LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
+ NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+RegistryValueIterator::~RegistryValueIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+bool RegistryValueIterator::Valid() const {
+ // true while the iterator is valid
+ return key_ != NULL && index_ >= 0;
+}
+
+
+void RegistryValueIterator::operator ++ () {
+ // advance to the next entry in the folder
+ --index_;
+ Read();
+}
+
+
+bool RegistryValueIterator::Read() {
+ if (Valid()) {
+ DWORD ncount = sizeof(name_)/sizeof(*name_);
+ value_size_ = sizeof(value_);
+ LRESULT r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_,
+ reinterpret_cast<BYTE*>(value_), &value_size_);
+ if (ERROR_SUCCESS == r)
+ return true;
+ }
+
+ name_[0] = '\0';
+ value_[0] = '\0';
+ value_size_ = 0;
+ return false;
+}
+
+
+DWORD RegistryValueIterator::ValueCount() const {
+
+ DWORD count = 0;
+ HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+
+//
+// RegistryKeyIterator
+//
+
+
+RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
+ LPCTSTR folder_key) {
+ LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ } else {
+ DWORD count = 0;
+ HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ } else {
+ index_ = count - 1;
+ }
+ }
+
+ Read();
+}
+
+RegistryKeyIterator::~RegistryKeyIterator() {
+ if (key_)
+ ::RegCloseKey(key_);
+}
+
+bool RegistryKeyIterator::Valid() const {
+ // true while the iterator is valid
+ return key_ != NULL && index_ >= 0;
+}
+
+
+void RegistryKeyIterator::operator ++ () {
+ // advance to the next entry in the folder
+ --index_;
+ Read();
+}
+
+
+bool RegistryKeyIterator::Read() {
+ if (Valid()) {
+ DWORD ncount = sizeof(name_)/sizeof(*name_);
+ FILETIME written;
+ LRESULT r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
+ NULL, &written);
+ if (ERROR_SUCCESS == r)
+ return true;
+ }
+
+ name_[0] = '\0';
+ return false;
+}
+
+
+DWORD RegistryKeyIterator::SubkeyCount() const {
+
+ DWORD count = 0;
+ HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL);
+
+ if (result != ERROR_SUCCESS)
+ return 0;
+
+ return count;
+}
+
+
+//
+// RegKey
+//
+
+
+
+RegKey::RegKey(HKEY rootkey, const tchar* subkey, REGSAM access)
+ : key_(NULL), watch_event_(0) {
+ if (rootkey) {
+ if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
+ this->Create(rootkey, subkey, access);
+ else
+ this->Open(rootkey, subkey, access);
+ }
+ else assert(!subkey);
+}
+
+
+
+void RegKey::Close() {
+ StopWatching();
+ if (key_) {
+ ::RegCloseKey(key_);
+ key_ = NULL;
+ }
+}
+
+
+
+bool RegKey::Create(HKEY rootkey, const tchar* subkey, REGSAM access) {
+ DWORD disposition_value;
+ return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
+}
+
+
+
+bool RegKey::CreateWithDisposition(HKEY rootkey, const tchar* subkey,
+ DWORD* disposition, REGSAM access) {
+ assert(rootkey && subkey && access && disposition);
+ this->Close();
+
+ LONG const result = RegCreateKeyEx(rootkey,
+ subkey,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ access,
+ NULL,
+ &key_,
+ disposition );
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ return false;
+ }
+ else return true;
+}
+
+
+
+bool RegKey::Open(HKEY rootkey, const tchar* subkey, REGSAM access) {
+ assert(rootkey && subkey && access);
+ this->Close();
+
+ LONG const result = RegOpenKeyEx(rootkey, subkey, 0,
+ access, &key_ );
+ if (result != ERROR_SUCCESS) {
+ key_ = NULL;
+ return false;
+ }
+ else return true;
+}
+
+
+
+bool RegKey::CreateKey(const tchar* name, REGSAM access) {
+ assert(name && access);
+
+ HKEY subkey = NULL;
+ LONG const result = RegCreateKeyEx(key_, name, 0, NULL,
+ REG_OPTION_NON_VOLATILE,
+ access, NULL, &subkey, NULL);
+ this->Close();
+
+ key_ = subkey;
+ return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::OpenKey(const tchar* name, REGSAM access) {
+ assert(name && access);
+
+ HKEY subkey = NULL;
+ LONG const result = RegOpenKeyEx(key_, name, 0, access, &subkey);
+
+ this->Close();
+
+ key_ = subkey;
+ return (result == ERROR_SUCCESS);
+}
+
+
+
+
+DWORD RegKey::ValueCount() {
+ DWORD count = 0;
+ HRESULT const result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL,
+ NULL, &count, NULL, NULL, NULL, NULL);
+ return (result != ERROR_SUCCESS) ? 0 : count;
+}
+
+
+bool RegKey::ReadName(int index, tstr* name) {
+ tchar buf[256];
+ DWORD bufsize = sizeof(buf)/sizeof(*buf);
+ LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL,
+ NULL, NULL);
+ if (r != ERROR_SUCCESS)
+ return false;
+ if (name)
+ *name = buf;
+ return true;
+}
+
+
+bool RegKey::ValueExists(const tchar* name) {
+ if (!key_) return false;
+ const HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL);
+ return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::ReadValue(const tchar* name, void* data,
+ DWORD* dsize, DWORD* dtype) {
+ if (!key_) return false;
+ HRESULT const result = RegQueryValueEx(key_, name, 0, dtype,
+ reinterpret_cast<LPBYTE>(data),
+ dsize);
+ return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::ReadValue(const tchar* name, tstr * value) {
+ assert(value);
+ static const size_t kMaxStringLength = 1024; // This is after expansion.
+ // Use the one of the other forms of ReadValue if 1024 is too small for you.
+ TCHAR raw_value[kMaxStringLength];
+ DWORD type = REG_SZ, size = sizeof(raw_value);
+ if (this->ReadValue(name, raw_value, &size, &type)) {
+ if (type == REG_SZ) {
+ *value = raw_value;
+ } else if (type == REG_EXPAND_SZ) {
+ TCHAR expanded[kMaxStringLength];
+ size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
+ // Success: returns the number of TCHARs copied
+ // Fail: buffer too small, returns the size required
+ // Fail: other, returns 0
+ if (size == 0 || size > kMaxStringLength)
+ return false;
+ *value = expanded;
+ } else {
+ // Not a string. Oops.
+ return false;
+ }
+ return true;
+ }
+ else return false;
+}
+
+
+
+bool RegKey::ReadValueDW(const tchar* name, DWORD * value) {
+ assert(value);
+ DWORD type = REG_DWORD, size = sizeof(DWORD), result = 0;
+ if (this->ReadValue(name, &result, &size, &type)
+ && (type == REG_DWORD || type == REG_BINARY)
+ && size == sizeof(DWORD)) {
+ *value = result;
+ return true;
+ }
+ else return false;
+}
+
+
+
+bool RegKey::WriteValue(const tchar* name, const void * data, DWORD dsize, DWORD dtype) {
+ assert(data);
+ if (!key_) return false;
+ HRESULT const result = RegSetValueEx(key_, name, 0,
+ dtype,
+ reinterpret_cast<LPBYTE>(const_cast<void*>(data)),
+ dsize);
+ return (result == ERROR_SUCCESS);
+}
+
+
+
+bool RegKey::WriteValue(const tchar * name, const tchar * value) {
+ return this->WriteValue(name, value,
+ static_cast<DWORD>(sizeof(*value) * (_tcslen(value) + 1)), REG_SZ);
+}
+
+
+bool RegKey::WriteValue(const tchar * name, DWORD value) {
+ return this->WriteValue(name, &value,
+ static_cast<DWORD>(sizeof(value)), REG_DWORD);
+}
+
+
+
+bool RegKey::DeleteKey(const tchar * name) {
+ if (!key_) return false;
+ return (ERROR_SUCCESS == SHDeleteKey(key_, name));
+}
+
+
+bool RegKey::DeleteValue(const tchar * value_name) {
+ assert(value_name);
+ HRESULT const result = RegDeleteValue(key_, value_name);
+ return (result == ERROR_SUCCESS);
+}
+
+bool RegKey::StartWatching() {
+ assert(watch_event_ == 0);
+ watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ DWORD filter = REG_NOTIFY_CHANGE_NAME |
+ REG_NOTIFY_CHANGE_ATTRIBUTES |
+ REG_NOTIFY_CHANGE_LAST_SET |
+ REG_NOTIFY_CHANGE_SECURITY;
+
+ // Watch the registry key for a change of value.
+ HRESULT result = RegNotifyChangeKeyValue(key_, TRUE, filter,
+ watch_event_, TRUE);
+ if (SUCCEEDED(result)) {
+ return true;
+ } else {
+ CloseHandle(watch_event_);
+ watch_event_ = 0;
+ return false;
+ }
+}
+
+bool RegKey::StopWatching() {
+ if (watch_event_) {
+ CloseHandle(watch_event_);
+ watch_event_ = 0;
+ return true;
+ }
+ return false;
+}
+
+bool RegKey::HasChanged() {
+ if (watch_event_) {
+ if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
+ // An event only gets signaled once, then it's done, so we have
+ // to set up another event to watch.
+ CloseHandle(watch_event_);
+ watch_event_ = 0;
+ StartWatching();
+ return true;
+ }
+ }
+ return false;
+}
+
+
+// Register a COM object with the most usual properties.
+bool RegisterCOMServer(const tchar* guid, const tchar* name, const tchar* path) {
+ RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE);
+ key.CreateKey(guid, KEY_WRITE);
+ key.WriteValue(NULL, name);
+ key.CreateKey(_T("InprocServer32"), KEY_WRITE);
+ key.WriteValue(NULL, path);
+ key.WriteValue(_T("ThreadingModel"), _T("Apartment"));
+ return true;
+};
+
+bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module) {
+ tchar module_path[MAX_PATH];
+ ::GetModuleFileName(module, module_path, MAX_PATH);
+ _tcslwr_s(module_path, MAX_PATH);
+ return RegisterCOMServer(guid, name, module_path);
+}
+
+bool UnregisterCOMServer(const tchar* guid) {
+ RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE);
+ key.DeleteKey(guid);
+ return true;
+}
+
+// LocalWords: RegKey
diff --git a/base/registry.h b/base/registry.h
new file mode 100644
index 0000000..6c70ba7
--- /dev/null
+++ b/base/registry.h
@@ -0,0 +1,249 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#ifndef BASE_REGISTRY_H__
+#define BASE_REGISTRY_H__
+
+#include <windows.h>
+#include <tchar.h>
+#include <shlwapi.h>
+#include <string>
+
+// The shared file uses a bunch of header files that define types that we don't.
+// To avoid changing much code from the standard version, and also to avoid
+// polluting our namespace with extra types we don't want, we define these types
+// here with the preprocessor and undefine them at the end of the file.
+#define tchar TCHAR
+#define CTP const tchar*
+#define tstr std::basic_string<tchar>
+
+// RegKey
+// Utility class to read from and manipulate the registry.
+// Registry vocabulary primer: a "key" is like a folder, in which there
+// are "values", which are <name,data> pairs, with an associated data type.
+
+class RegKey {
+ public:
+ RegKey(HKEY rootkey = NULL, CTP subkey = NULL, REGSAM access = KEY_READ);
+ // start there
+
+ ~RegKey() { this->Close(); }
+
+ bool Create(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ);
+
+ bool CreateWithDisposition(HKEY rootkey, CTP subkey, DWORD* disposition,
+ REGSAM access = KEY_READ);
+
+ bool Open(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ);
+
+ // Create a subkey (or open if exists)
+ bool CreateKey(CTP name, REGSAM access);
+
+ // Open a subkey
+ bool OpenKey(CTP name, REGSAM access);
+
+ // all done, eh?
+ void Close();
+
+ DWORD ValueCount(); // Count of the number of value extant
+
+ bool ReadName(int index, tstr* name); // Determine the Nth value's name
+
+ // True while the key is valid
+ bool Valid() const { return NULL != key_; }
+
+ // Kill key and everything that liveth below it; please be careful out there
+ bool DeleteKey(CTP name);
+
+ // Delete a single value within the key
+ bool DeleteValue(CTP name);
+
+ bool ValueExists(CTP name);
+ bool ReadValue(CTP name, void * data, DWORD * dsize, DWORD * dtype = NULL);
+ bool ReadValue(CTP name, tstr * value);
+ bool ReadValueDW(CTP name, DWORD * value); // Named to differ from tstr*
+
+ bool WriteValue(CTP name, const void * data, DWORD dsize,
+ DWORD dtype = REG_BINARY);
+ bool WriteValue(CTP name, CTP value);
+ bool WriteValue(CTP name, DWORD value);
+
+ // StartWatching()
+ // Start watching the key to see if any of its values have changed.
+ // The key must have been opened with the KEY_NOTIFY access
+ // privelege.
+ bool StartWatching();
+
+ // HasChanged()
+ // If StartWatching hasn't been called, always returns false.
+ // Otherwise, returns true if anything under the key has changed.
+ // This can't be const because the watch_event_ may be refreshed.
+ bool HasChanged();
+
+ // StopWatching()
+ // Will automatically be called by destructor if not manually called
+ // beforehand. Returns true if it was watching, false otherwise.
+ bool StopWatching();
+
+ inline bool IsWatching() const { return watch_event_ != 0; }
+ HKEY Handle() const { return key_; }
+
+ private:
+ HKEY key_; // the registry key being iterated
+ HANDLE watch_event_;
+};
+
+
+// Standalone registry functions -- sorta deprecated, they now map to
+// using RegKey
+
+
+// Add a raw data to the registry -- you can pass NULL for the data if
+// you just want to create a key
+inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name,
+ void const * data, DWORD dsize,
+ DWORD dtype = REG_BINARY) {
+ return RegKey(root_key, key, KEY_WRITE).WriteValue(value_name, data, dsize,
+ dtype);
+}
+
+// Convenience routine to add a string value to the registry
+inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name, CTP value) {
+ return AddToRegistry(root_key, key, value_name, value,
+ sizeof(*value) * (lstrlen(value) + 1), REG_SZ);
+}
+
+// Read raw data from the registry -- pass something as the dtype
+// parameter if you care to learn what type the value was stored as
+inline bool ReadFromRegistry(HKEY root_key, CTP key, CTP value_name,
+ void* data, DWORD* dsize, DWORD* dtype = NULL) {
+ return RegKey(root_key, key).ReadValue(value_name, data, dsize, dtype);
+}
+
+
+// Delete a value or a key from the registry
+inline bool DeleteFromRegistry(HKEY root_key, CTP subkey, CTP value_name) {
+ if (value_name)
+ return ERROR_SUCCESS == ::SHDeleteValue(root_key, subkey, value_name);
+ else
+ return ERROR_SUCCESS == ::SHDeleteKey(root_key, subkey);
+}
+
+
+
+// delete a key and all subkeys from the registry
+inline bool DeleteKeyFromRegistry(HKEY root_key, CTP key_path, CTP key_name) {
+ RegKey key;
+ return key.Open(root_key, key_path, KEY_WRITE)
+ && key.DeleteKey(key_name);
+}
+
+
+// Iterates the entries found in a particular folder on the registry.
+// For this application I happen to know I wont need data size larger
+// than MAX_PATH, but in real life this wouldn't neccessarily be
+// adequate.
+class RegistryValueIterator {
+ public:
+ // Specify a key in construction
+ RegistryValueIterator(HKEY root_key, LPCTSTR folder_key);
+
+ ~RegistryValueIterator();
+
+ DWORD ValueCount() const; // count of the number of subkeys extant
+
+ bool Valid() const; // true while the iterator is valid
+
+ void operator++(); // advance to the next entry in the folder
+
+ // The pointers returned by these functions are statics owned by the
+ // Name and Value functions
+ CTP Name() const { return name_; }
+ CTP Value() const { return value_; }
+ DWORD ValueSize() const { return value_size_; }
+ DWORD Type() const { return type_; }
+
+ int Index() const { return index_; }
+
+ private:
+ bool Read(); // read in the current values
+
+ HKEY key_; // the registry key being iterated
+ int index_; // current index of the iteration
+
+ // Current values
+ TCHAR name_[MAX_PATH];
+ TCHAR value_[MAX_PATH];
+ DWORD value_size_;
+ DWORD type_;
+};
+
+
+class RegistryKeyIterator {
+ public:
+ // Specify a parent key in construction
+ RegistryKeyIterator(HKEY root_key, LPCTSTR folder_key);
+
+ ~RegistryKeyIterator();
+
+ DWORD SubkeyCount() const; // count of the number of subkeys extant
+
+ bool Valid() const; // true while the iterator is valid
+
+ void operator++(); // advance to the next entry in the folder
+
+ // The pointer returned by Name() is a static owned by the function
+ CTP Name() const { return name_; }
+
+ int Index() const { return index_; }
+
+ private:
+ bool Read(); // read in the current values
+
+ HKEY key_; // the registry key being iterated
+ int index_; // current index of the iteration
+
+ // Current values
+ TCHAR name_[MAX_PATH];
+};
+
+
+// Register a COM object with the most usual properties.
+bool RegisterCOMServer(const tchar* guid, const tchar* name,
+ const tchar* modulepath);
+bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module);
+bool UnregisterCOMServer(const tchar* guid);
+
+// undo the local types defined above
+#undef tchar
+#undef CTP
+#undef tstr
+
+#endif // BASE_REGISTRY_H__
diff --git a/base/resource_util.cc b/base/resource_util.cc
new file mode 100644
index 0000000..e3b5504
--- /dev/null
+++ b/base/resource_util.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/resource_util.h"
+
+#include "base/logging.h"
+
+namespace base {
+bool GetDataResourceFromModule(HMODULE module, int resource_id,
+ void** data, size_t* length) {
+ if (!module)
+ return false;
+
+ // Get a pointer to the data in the dll.
+ DCHECK(IS_INTRESOURCE(resource_id));
+ HRSRC hres_info = FindResource(module, MAKEINTRESOURCE(resource_id),
+ L"BINDATA");
+ if (NULL == hres_info)
+ return false;
+
+ DWORD data_size = SizeofResource(module, hres_info);
+
+ HGLOBAL hres = LoadResource(module, hres_info);
+ if (!hres_info)
+ return false;
+
+ *data = LockResource(hres);
+ *length = static_cast<size_t>(data_size);
+ return true;
+}
+} // namespace
diff --git a/base/resource_util.h b/base/resource_util.h
new file mode 100644
index 0000000..b592cc2
--- /dev/null
+++ b/base/resource_util.h
@@ -0,0 +1,49 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file contains utility functions for accessing resources in external
+// files (DLLs) or embedded in the executable itself.
+
+#ifndef BASE_RESOURCE_UTIL_H__
+#define BASE_RESOURCE_UTIL_H__
+
+#include <windows.h>
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace base {
+// Function for getting a data resource (BINDATA) from a dll. Some
+// resources are optional, especially in unit tests, so this returns false
+// but doesn't raise an error if the resource can't be loaded.
+bool GetDataResourceFromModule(HMODULE module, int resource_id,
+ void** data, size_t* length);
+} // namespace
+
+#endif // BASE_RESOURCE_UTIL_H__
diff --git a/base/revocable_store.cc b/base/revocable_store.cc
new file mode 100644
index 0000000..2974792
--- /dev/null
+++ b/base/revocable_store.cc
@@ -0,0 +1,72 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/revocable_store.h"
+
+#include "base/logging.h"
+
+RevocableStore::Revocable::Revocable(RevocableStore* store)
+ : store_reference_(store->owning_reference_) {
+ // We AddRef() the owning reference.
+ DCHECK(store_reference_->store());
+ store_reference_->store()->Add(this);
+}
+
+RevocableStore::Revocable::~Revocable() {
+ if (!revoked()) {
+ // Notify the store of our destruction.
+ --(store_reference_->store()->count_);
+ }
+}
+
+RevocableStore::RevocableStore() : count_(0) {
+ // Create a new owning reference.
+ owning_reference_ = new StoreRef(this);
+}
+
+RevocableStore::~RevocableStore() {
+ // Revoke all the items in the store.
+ owning_reference_->set_store(NULL);
+}
+
+void RevocableStore::Add(Revocable* item) {
+ DCHECK(!item->revoked());
+ ++count_;
+}
+
+void RevocableStore::RevokeAll() {
+ // We revoke all the existing items in the store and reset our count.
+ owning_reference_->set_store(NULL);
+ count_ = 0;
+
+ // Then we create a new owning reference for new items that get added.
+ // This Release()s the old owning reference, allowing it to be freed after
+ // all the items that were in the store are eventually destroyed.
+ owning_reference_ = new StoreRef(this);
+}
diff --git a/base/revocable_store.h b/base/revocable_store.h
new file mode 100644
index 0000000..cee1c64
--- /dev/null
+++ b/base/revocable_store.h
@@ -0,0 +1,101 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_REVOCABLE_STORE_H__
+#define BASE_REVOCABLE_STORE_H__
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/ref_counted.h"
+
+// |RevocableStore| is a container of items that can be removed from the store.
+class RevocableStore {
+ public:
+ // A |StoreRef| is used to link the |RevocableStore| to its items. There is
+ // one StoreRef per store, and each item holds a reference to it. If the
+ // store wishes to revoke its items, it sets |store_| to null. Items are
+ // permitted to release their reference to the |StoreRef| when they no longer
+ // require the store.
+ class StoreRef : public base::RefCounted<StoreRef> {
+ public:
+ StoreRef(RevocableStore* store) : store_(store) { }
+
+ void set_store(RevocableStore* store) { store_ = store; }
+ RevocableStore* store() const { return store_; }
+
+ private:
+ RevocableStore* store_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StoreRef);
+ };
+
+ // An item in the store. On construction, the object adds itself to the
+ // store.
+ class Revocable {
+ public:
+ Revocable(RevocableStore* store);
+ ~Revocable();
+
+ // This item has been revoked if it no longer has a pointer to the store.
+ bool revoked() const { return !store_reference_->store(); }
+
+ private:
+ // We hold a reference to the store through this ref pointer. We release
+ // this reference on destruction.
+ scoped_refptr<StoreRef> store_reference_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Revocable);
+ };
+
+ RevocableStore();
+ ~RevocableStore();
+
+ // Revokes all the items in the store.
+ void RevokeAll();
+
+ // Returns true if there are no items in the store.
+ bool empty() const { return count_ == 0; }
+
+ private:
+ friend class Revocable;
+
+ // Adds an item to the store. To add an item to the store, construct it
+ // with a pointer to the store.
+ void Add(Revocable* item);
+
+ // This is the reference the unrevoked items in the store hold.
+ scoped_refptr<StoreRef> owning_reference_;
+
+ // The number of unrevoked items in the store.
+ int count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RevocableStore);
+};
+
+#endif // BASE_REVOCABLE_STORE_H__
diff --git a/base/run_all_perftests.cc b/base/run_all_perftests.cc
new file mode 100644
index 0000000..4ef7494
--- /dev/null
+++ b/base/run_all_perftests.cc
@@ -0,0 +1,54 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/perftimer.h"
+#include "base/string_util.h"
+#include "base/test_suite.h"
+
+int main(int argc, char** argv) {
+ // Initialize the perf timer log
+ std::string log_file =
+ WideToUTF8(CommandLine().GetSwitchValue(L"log-file"));
+ if (log_file.empty())
+ log_file = "perf_test.log";
+ ASSERT_TRUE(InitPerfLog(log_file.c_str()));
+
+ // Raise to high priority to have more precise measurements. Since we don't
+ // aim at 1% precision, it is not necessary to run at realtime level.
+ if (!IsDebuggerPresent())
+ SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
+
+ int rv = TestSuite(argc, argv).Run();
+
+ FinalizePerfLog();
+ return rv;
+}
diff --git a/base/run_all_unittests.cc b/base/run_all_unittests.cc
new file mode 100644
index 0000000..69b2d3f
--- /dev/null
+++ b/base/run_all_unittests.cc
@@ -0,0 +1,34 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/test_suite.h"
+
+int main(int argc, char** argv) {
+ return TestSuite(argc, argv).Run();
+}
diff --git a/base/scoped_cftyperef.h b/base/scoped_cftyperef.h
new file mode 100644
index 0000000..f4623fe
--- /dev/null
+++ b/base/scoped_cftyperef.h
@@ -0,0 +1,97 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SCOPED_CFTYPEREF_H__
+#define BASE_SCOPED_CFTYPEREF_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+// scoped_cftyperef<> is patterned after scoped_ptr<>, but maintains ownership
+// of a CoreFoundation object: any object that can be represented as a
+// CFTypeRef. Style deviations here are solely for compatibility with
+// scoped_ptr<>'s interface, with which everyone is already familiar.
+template<typename CFT>
+class scoped_cftyperef {
+ public:
+ typedef CFT element_type;
+
+ explicit scoped_cftyperef(CFT object = NULL)
+ : object_(object) {
+ }
+
+ ~scoped_cftyperef() {
+ if (object_)
+ CFRelease(object_);
+ }
+
+ void reset(CFT object = NULL) {
+ if (object_ && object_ != object) {
+ CFRelease(object_);
+ object_ = object;
+ }
+ }
+
+ bool operator==(CFT that) const {
+ return object_ == that;
+ }
+
+ bool operator!=(CFT that) const {
+ return object_ != that;
+ }
+
+ operator CFT() const {
+ return object_;
+ }
+
+ CFT get() const {
+ return object_;
+ }
+
+ void swap(scoped_cftyperef& that) {
+ CFT temp = that.object_;
+ that.object_ = object_;
+ object_ = temp;
+ }
+
+ // scoped_cftyperef<>::release() is like scoped_ptr<>::release. It is NOT
+ // a wrapper for CFRelease(). To force a scoped_cftyperef<> object to call
+ // CFRelease(), use scoped_cftyperef<>::reset().
+ CFT release() {
+ CFT temp = object_;
+ object_ = NULL;
+ return temp;
+ }
+
+ private:
+ CFT object_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(scoped_cftyperef);
+};
+
+#endif // BASE_SCOPED_CFTYPEREF_H__
diff --git a/base/scoped_handle.h b/base/scoped_handle.h
new file mode 100644
index 0000000..63f8d00
--- /dev/null
+++ b/base/scoped_handle.h
@@ -0,0 +1,213 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SCOPED_HANDLE_H__
+#define BASE_SCOPED_HANDLE_H__
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+
+// Used so we always remember to close the handle. Example:
+// ScopedHandle hfile(CreateFile(...));
+// if (!hfile.Get())
+// ...process error
+// ReadFile(hfile.Get(), ...);
+//
+// To sqirrel the handle away somewhere else:
+// secret_handle_ = hfile.Take();
+//
+// To explicitly close the handle:
+// CloseHandle(hfile.Take());
+class ScopedHandle {
+ public:
+ ScopedHandle() : handle_(NULL) {
+ }
+
+ explicit ScopedHandle(HANDLE h) : handle_(NULL) {
+ Set(h);
+ }
+
+ ~ScopedHandle() {
+ Close();
+ }
+
+ // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL
+ // usage for errors.
+ bool IsValid() const {
+ return handle_ != NULL;
+ }
+
+ void Set(HANDLE new_handle) {
+ Close();
+
+ // Windows is inconsistent about invalid handles, so we always use NULL
+ if (handle_ != INVALID_HANDLE_VALUE)
+ handle_ = new_handle;
+ }
+
+ HANDLE Get() {
+ return handle_;
+ }
+
+ operator HANDLE() { return handle_; }
+
+ HANDLE Take() {
+ // transfers ownership away from this object
+ HANDLE h = handle_;
+ handle_ = NULL;
+ return h;
+ }
+
+ private:
+ void Close() {
+ if (handle_) {
+ CloseHandle(handle_);
+ handle_ = NULL;
+ }
+ }
+
+ HANDLE handle_;
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedHandle);
+};
+
+// Like ScopedHandle, but for HANDLEs returned from FindFile().
+class ScopedFindFileHandle {
+ public:
+ explicit ScopedFindFileHandle(HANDLE handle) : handle_(handle) {
+ // Windows is inconsistent about invalid handles, so we always use NULL
+ if (handle_ == INVALID_HANDLE_VALUE)
+ handle_ = NULL;
+ }
+
+ ~ScopedFindFileHandle() {
+ if (handle_)
+ FindClose(handle_);
+ }
+
+ // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL
+ // usage for errors.
+ bool IsValid() const { return handle_ != NULL; }
+
+ operator HANDLE() { return handle_; }
+
+ private:
+ HANDLE handle_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedFindFileHandle);
+};
+
+// Like ScopedHandle but for HDC. Only use this on HDCs returned from
+// CreateCompatibleDC. For an HDC returned by GetDC, use ReleaseDC instead.
+class ScopedHDC {
+ public:
+ explicit ScopedHDC(HDC h) : hdc_(h) { }
+
+ ~ScopedHDC() {
+ if (hdc_)
+ DeleteDC(hdc_);
+ }
+
+ operator HDC() { return hdc_; }
+
+ private:
+ HDC hdc_;
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedHDC);
+};
+
+// Like ScopedHandle but for HBITMAP.
+class ScopedBitmap {
+ public:
+ explicit ScopedBitmap(HBITMAP h) : hbitmap_(h) { }
+
+ ~ScopedBitmap() {
+ if (hbitmap_)
+ DeleteObject(hbitmap_);
+ }
+
+ operator HBITMAP() { return hbitmap_; }
+
+ private:
+ HBITMAP hbitmap_;
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedBitmap);
+};
+
+// Like ScopedHandle but for HRGN.
+class ScopedHRGN {
+ public:
+ explicit ScopedHRGN(HRGN h) : hrgn_(h) { }
+
+ ~ScopedHRGN() {
+ if (hrgn_)
+ DeleteObject(hrgn_);
+ }
+
+ operator HRGN() { return hrgn_; }
+
+ ScopedHRGN& operator=(HRGN hrgn) {
+ if (hrgn_ && hrgn != hrgn_)
+ DeleteObject(hrgn_);
+ hrgn_ = hrgn;
+ return *this;
+ }
+
+ private:
+ HRGN hrgn_;
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedHRGN);
+};
+
+// Like ScopedHandle except for HGLOBAL.
+template<class T>
+class ScopedHGlobal {
+ public:
+ explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) {
+ data_ = static_cast<T*>(GlobalLock(glob_));
+ }
+ ~ScopedHGlobal() {
+ GlobalUnlock(glob_);
+ }
+
+ T* get() { return data_; }
+
+ size_t Size() const { return GlobalSize(glob_); }
+
+ T* operator->() const {
+ assert(data_ != 0);
+ return data_;
+ }
+
+ private:
+ HGLOBAL glob_;
+
+ T* data_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedHGlobal);
+};
+
+#endif // BASE_SCOPED_HANDLE_H__
diff --git a/base/scoped_ptr.h b/base/scoped_ptr.h
new file mode 100644
index 0000000..0c9ea95
--- /dev/null
+++ b/base/scoped_ptr.h
@@ -0,0 +1,402 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// All Rights Reserved.
+
+#ifndef BASE_SCOPED_PTR_H__
+#define BASE_SCOPED_PTR_H__
+
+// This is an implementation designed to match the anticipated future TR2
+// implementation of the scoped_ptr class, and its closely-related brethren,
+// scoped_array, scoped_ptr_malloc, and make_scoped_ptr.
+//
+// See http://wiki/Main/ScopedPointerInterface for the spec that drove this
+// file.
+
+#include <assert.h>
+#include <stdlib.h>
+#include <cstddef>
+
+
+template <class C> class scoped_ptr;
+template <class C, class Free> class scoped_ptr_malloc;
+template <class C> class scoped_array;
+
+template <class C>
+scoped_ptr<C> make_scoped_ptr(C *);
+
+// A scoped_ptr<T> is like a T*, except that the destructor of scoped_ptr<T>
+// automatically deletes the pointer it holds (if any).
+// That is, scoped_ptr<T> owns the T object that it points to.
+// Like a T*, a scoped_ptr<T> may hold either NULL or a pointer to a T object.
+// Also like T*, scoped_ptr<T> is thread-compatible, and once you
+// dereference it, you get the threadsafety guarantees of T.
+//
+// The size of a scoped_ptr is small:
+// sizeof(scoped_ptr<C>) == sizeof(C*)
+template <class C>
+class scoped_ptr {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with new.
+ explicit scoped_ptr(C* p = NULL) : ptr_(p) { }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_ptr() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != ptr_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete ptr_;
+ ptr_ = p;
+ }
+ }
+
+ // Accessors to get the owned object.
+ // operator* and operator-> will assert() if there is no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+ C* get() const { return ptr_; }
+
+ // Comparison operators.
+ // These return whether two scoped_ptr refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return ptr_ == p; }
+ bool operator!=(C* p) const { return ptr_ != p; }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr& p2) {
+ C* tmp = ptr_;
+ ptr_ = p2.ptr_;
+ p2.ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* retVal = ptr_;
+ ptr_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* ptr_;
+
+ friend scoped_ptr<C> make_scoped_ptr<C>(C *p);
+
+ // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't
+ // make sense, and if C2 == C, it still doesn't make sense because you should
+ // never have the same object owned by two different scoped_ptrs.
+ template <class C2> bool operator==(scoped_ptr<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_ptr<C2> const& p2) const;
+
+ // Disallow evil constructors
+ scoped_ptr(const scoped_ptr&);
+ void operator=(const scoped_ptr&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_ptr<C>& p1, scoped_ptr<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_ptr<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_ptr<C>& p2) {
+ return p1 != p2.get();
+}
+
+template <class C>
+scoped_ptr<C> make_scoped_ptr(C *p) {
+ // This does nothing but to return a scoped_ptr of the type that the passed
+ // pointer is of. (This eliminates the need to specify the name of T when
+ // making a scoped_ptr that is used anonymously/temporarily.) From an
+ // access control point of view, we construct an unnamed scoped_ptr here
+ // which we return and thus copy-construct. Hence, we need to have access
+ // to scoped_ptr::scoped_ptr(scoped_ptr const &). However, it is guaranteed
+ // that we never actually call the copy constructor, which is a good thing
+ // as we would call the temporary's object destructor (and thus delete p)
+ // if we actually did copy some object, here.
+ return scoped_ptr<C>(p);
+}
+
+// scoped_array<C> is like scoped_ptr<C>, except that the caller must allocate
+// with new [] and the destructor deletes objects with delete [].
+//
+// As with scoped_ptr<C>, a scoped_array<C> either points to an object
+// or is NULL. A scoped_array<C> owns the object that it points to.
+// scoped_array<T> is thread-compatible, and once you index into it,
+// the returned objects have only the threadsafety guarantees of T.
+//
+// Size: sizeof(scoped_array<C>) == sizeof(C*)
+template <class C>
+class scoped_array {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_array.
+ // The input parameter must be allocated with new [].
+ explicit scoped_array(C* p = NULL) : array_(p) { }
+
+ // Destructor. If there is a C object, delete it.
+ // We don't need to test ptr_ == NULL because C++ does that for us.
+ ~scoped_array() {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ }
+
+ // Reset. Deletes the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (p != array_) {
+ enum { type_must_be_complete = sizeof(C) };
+ delete[] array_;
+ array_ = p;
+ }
+ }
+
+ // Get one element of the current object.
+ // Will assert() if there is no current object, or index i is negative.
+ C& operator[](std::ptrdiff_t i) const {
+ assert(i >= 0);
+ assert(array_ != NULL);
+ return array_[i];
+ }
+
+ // Get a pointer to the zeroth element of the current object.
+ // If there is no current object, return NULL.
+ C* get() const {
+ return array_;
+ }
+
+ // Comparison operators.
+ // These return whether two scoped_array refer to the same object, not just to
+ // two different but equal objects.
+ bool operator==(C* p) const { return array_ == p; }
+ bool operator!=(C* p) const { return array_ != p; }
+
+ // Swap two scoped arrays.
+ void swap(scoped_array& p2) {
+ C* tmp = array_;
+ array_ = p2.array_;
+ p2.array_ = tmp;
+ }
+
+ // Release an array.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* retVal = array_;
+ array_ = NULL;
+ return retVal;
+ }
+
+ private:
+ C* array_;
+
+ // Forbid comparison of different scoped_array types.
+ template <class C2> bool operator==(scoped_array<C2> const& p2) const;
+ template <class C2> bool operator!=(scoped_array<C2> const& p2) const;
+
+ // Disallow evil constructors
+ scoped_array(const scoped_array&);
+ void operator=(const scoped_array&);
+};
+
+// Free functions
+template <class C>
+void swap(scoped_array<C>& p1, scoped_array<C>& p2) {
+ p1.swap(p2);
+}
+
+template <class C>
+bool operator==(C* p1, const scoped_array<C>& p2) {
+ return p1 == p2.get();
+}
+
+template <class C>
+bool operator!=(C* p1, const scoped_array<C>& p2) {
+ return p1 != p2.get();
+}
+
+// This class wraps the c library function free() in a class that can be
+// passed as a template argument to scoped_ptr_malloc below.
+class ScopedPtrMallocFree {
+ public:
+ inline void operator()(void* x) const {
+ free(x);
+ }
+};
+
+// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
+// second template argument, the functor used to free the object.
+
+template<class C, class FreeProc = ScopedPtrMallocFree>
+class scoped_ptr_malloc {
+ public:
+
+ // The element type
+ typedef C element_type;
+
+ // Constructor. Defaults to intializing with NULL.
+ // There is no way to create an uninitialized scoped_ptr.
+ // The input parameter must be allocated with an allocator that matches the
+ // Free functor. For the default Free functor, this is malloc, calloc, or
+ // realloc.
+ explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {}
+
+ // Destructor. If there is a C object, call the Free functor.
+ ~scoped_ptr_malloc() {
+ free_(ptr_);
+ }
+
+ // Reset. Calls the Free functor on the current owned object, if any.
+ // Then takes ownership of a new object, if given.
+ // this->reset(this->get()) works.
+ void reset(C* p = NULL) {
+ if (ptr_ != p) {
+ free_(ptr_);
+ ptr_ = p;
+ }
+ }
+
+ // Get the current object.
+ // operator* and operator-> will cause an assert() failure if there is
+ // no current object.
+ C& operator*() const {
+ assert(ptr_ != NULL);
+ return *ptr_;
+ }
+
+ C* operator->() const {
+ assert(ptr_ != NULL);
+ return ptr_;
+ }
+
+ C* get() const {
+ return ptr_;
+ }
+
+ // Comparison operators.
+ // These return whether a scoped_ptr_malloc and a plain pointer refer
+ // to the same object, not just to two different but equal objects.
+ // For compatibility wwith the boost-derived implementation, these
+ // take non-const arguments.
+ bool operator==(C* p) const {
+ return ptr_ == p;
+ }
+
+ bool operator!=(C* p) const {
+ return ptr_ != p;
+ }
+
+ // Swap two scoped pointers.
+ void swap(scoped_ptr_malloc & b) {
+ C* tmp = b.ptr_;
+ b.ptr_ = ptr_;
+ ptr_ = tmp;
+ }
+
+ // Release a pointer.
+ // The return value is the current pointer held by this object.
+ // If this object holds a NULL pointer, the return value is NULL.
+ // After this operation, this object will hold a NULL pointer,
+ // and will not own the object any more.
+ C* release() {
+ C* tmp = ptr_;
+ ptr_ = NULL;
+ return tmp;
+ }
+
+ private:
+ C* ptr_;
+
+ // no reason to use these: each scoped_ptr_malloc should have its own object
+ template <class C2, class GP>
+ bool operator==(scoped_ptr_malloc<C2, GP> const& p) const;
+ template <class C2, class GP>
+ bool operator!=(scoped_ptr_malloc<C2, GP> const& p) const;
+
+ static FreeProc const free_;
+
+ // Disallow evil constructors
+ scoped_ptr_malloc(const scoped_ptr_malloc&);
+ void operator=(const scoped_ptr_malloc&);
+};
+
+template<class C, class FP>
+FP const scoped_ptr_malloc<C, FP>::free_ = FP();
+
+template<class C, class FP> inline
+void swap(scoped_ptr_malloc<C, FP>& a, scoped_ptr_malloc<C, FP>& b) {
+ a.swap(b);
+}
+
+template<class C, class FP> inline
+bool operator==(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p == b.get();
+}
+
+template<class C, class FP> inline
+bool operator!=(C* p, const scoped_ptr_malloc<C, FP>& b) {
+ return p != b.get();
+}
+
+#endif // BASE_SCOPED_PTR_H__
diff --git a/base/sha2.cc b/base/sha2.cc
new file mode 100644
index 0000000..4737a7c
--- /dev/null
+++ b/base/sha2.cc
@@ -0,0 +1,47 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/sha2.h"
+
+#include "base/third_party/nss/blapi.h"
+#include "base/third_party/nss/sha256.h"
+
+namespace base {
+
+void SHA256HashString(const std::string& str, void* output, size_t len) {
+ SHA256Context ctx;
+
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, reinterpret_cast<const unsigned char*>(str.data()),
+ static_cast<unsigned int>(str.length()));
+ SHA256_End(&ctx, static_cast<unsigned char*>(output), NULL,
+ static_cast<unsigned int>(len));
+}
+
+} // namespace base
diff --git a/base/sha2.h b/base/sha2.h
new file mode 100644
index 0000000..b9d5fb2
--- /dev/null
+++ b/base/sha2.h
@@ -0,0 +1,52 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHA2_H__
+#define BASE_SHA2_H__
+
+#include <string>
+
+namespace base {
+
+// These functions perform SHA-256 operations.
+//
+// Functions for SHA-384 and SHA-512 can be added when the need arises.
+
+enum {
+ SHA256_LENGTH = 32 // length in bytes of a SHA-256 hash
+};
+
+// Computes the SHA-256 hash of the input string 'str' and stores the first
+// 'len' bytes of the hash in the output buffer 'output'. If 'len' > 32,
+// only 32 bytes (the full hash) are stored in the 'output' buffer.
+void SHA256HashString(const std::string& str, void* output, size_t len);
+
+} // namespace base
+
+#endif // BASE_SHA2_H__
diff --git a/base/sha2_unittest.cc b/base/sha2_unittest.cc
new file mode 100644
index 0000000..c0d6343
--- /dev/null
+++ b/base/sha2_unittest.cc
@@ -0,0 +1,103 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/sha2.h"
+
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(Sha256Test, Test1) {
+ // Example B.1 from FIPS 180-2: one-block message.
+ std::string input1 = "abc";
+ int expected1[] = { 0xba, 0x78, 0x16, 0xbf,
+ 0x8f, 0x01, 0xcf, 0xea,
+ 0x41, 0x41, 0x40, 0xde,
+ 0x5d, 0xae, 0x22, 0x23,
+ 0xb0, 0x03, 0x61, 0xa3,
+ 0x96, 0x17, 0x7a, 0x9c,
+ 0xb4, 0x10, 0xff, 0x61,
+ 0xf2, 0x00, 0x15, 0xad };
+
+ uint8 output1[base::SHA256_LENGTH];
+ base::SHA256HashString(input1, output1, sizeof(output1));
+ for (int i = 0; i < base::SHA256_LENGTH; i++)
+ EXPECT_EQ(expected1[i], static_cast<int>(output1[i]));
+
+ uint8 output_truncated1[4]; // 4 bytes == 32 bits
+ base::SHA256HashString(input1, output_truncated1, sizeof(output_truncated1));
+ for (int i = 0; i < sizeof(output_truncated1); i++)
+ EXPECT_EQ(expected1[i], static_cast<int>(output_truncated1[i]));
+}
+
+TEST(Sha256Test, Test2) {
+ // Example B.2 from FIPS 180-2: multi-block message.
+ std::string input2 =
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ int expected2[] = { 0x24, 0x8d, 0x6a, 0x61,
+ 0xd2, 0x06, 0x38, 0xb8,
+ 0xe5, 0xc0, 0x26, 0x93,
+ 0x0c, 0x3e, 0x60, 0x39,
+ 0xa3, 0x3c, 0xe4, 0x59,
+ 0x64, 0xff, 0x21, 0x67,
+ 0xf6, 0xec, 0xed, 0xd4,
+ 0x19, 0xdb, 0x06, 0xc1 };
+
+ uint8 output2[base::SHA256_LENGTH];
+ base::SHA256HashString(input2, output2, sizeof(output2));
+ for (int i = 0; i < base::SHA256_LENGTH; i++)
+ EXPECT_EQ(expected2[i], static_cast<int>(output2[i]));
+
+ uint8 output_truncated2[6];
+ base::SHA256HashString(input2, output_truncated2, sizeof(output_truncated2));
+ for (int i = 0; i < sizeof(output_truncated2); i++)
+ EXPECT_EQ(expected2[i], static_cast<int>(output_truncated2[i]));
+}
+
+TEST(Sha256Test, Test3) {
+ // Example B.3 from FIPS 180-2: long message.
+ std::string input3(1000000, 'a'); // 'a' repeated a million times
+ int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c,
+ 0x99, 0x14, 0xfb, 0x92,
+ 0x81, 0xa1, 0xc7, 0xe2,
+ 0x84, 0xd7, 0x3e, 0x67,
+ 0xf1, 0x80, 0x9a, 0x48,
+ 0xa4, 0x97, 0x20, 0x0e,
+ 0x04, 0x6d, 0x39, 0xcc,
+ 0xc7, 0x11, 0x2c, 0xd0 };
+
+ uint8 output3[base::SHA256_LENGTH];
+ base::SHA256HashString(input3, output3, sizeof(output3));
+ for (int i = 0; i < base::SHA256_LENGTH; i++)
+ EXPECT_EQ(expected3[i], static_cast<int>(output3[i]));
+
+ uint8 output_truncated3[12];
+ base::SHA256HashString(input3, output_truncated3, sizeof(output_truncated3));
+ for (int i = 0; i < sizeof(output_truncated3); i++)
+ EXPECT_EQ(expected3[i], static_cast<int>(output_truncated3[i]));
+}
diff --git a/base/shared_event.cc b/base/shared_event.cc
new file mode 100644
index 0000000..e0870cf
--- /dev/null
+++ b/base/shared_event.cc
@@ -0,0 +1,104 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/shared_event.h"
+#include "base/logging.h"
+#include "base/time.h"
+
+SharedEvent::~SharedEvent() {
+ Close();
+}
+
+bool SharedEvent::Create(bool manual_reset, bool initial_state) {
+ DCHECK(!event_handle_);
+ event_handle_ = CreateEvent(NULL /* security attributes */, manual_reset,
+ initial_state, NULL /* name */);
+ DCHECK(event_handle_);
+ return !!event_handle_;
+}
+
+void SharedEvent::Close() {
+ if (event_handle_) {
+ BOOL rv = CloseHandle(event_handle_);
+ DCHECK(rv);
+ event_handle_ = NULL;
+ }
+}
+
+bool SharedEvent::SetSignaledState(bool signaled) {
+ DCHECK(event_handle_);
+ BOOL rv;
+ if (signaled) {
+ rv = SetEvent(event_handle_);
+ } else {
+ rv = ResetEvent(event_handle_);
+ }
+ return rv ? true : false;
+}
+
+bool SharedEvent::IsSignaled() {
+ DCHECK(event_handle_);
+ DWORD event_state = ::WaitForSingleObject(event_handle_, 0);
+ DCHECK(WAIT_OBJECT_0 == event_state || WAIT_TIMEOUT == event_state);
+ return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::WaitUntilSignaled(const TimeDelta& timeout) {
+ DCHECK(event_handle_);
+ DWORD event_state = ::WaitForSingleObject(event_handle_,
+ static_cast<DWORD>(timeout.InMillisecondsF()));
+ return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::WaitForeverUntilSignaled() {
+ DCHECK(event_handle_);
+ DWORD event_state = ::WaitForSingleObject(event_handle_,
+ INFINITE);
+ return event_state == WAIT_OBJECT_0;
+}
+
+bool SharedEvent::ShareToProcess(ProcessHandle process,
+ SharedEventHandle *new_handle) {
+ DCHECK(event_handle_);
+ HANDLE event_handle_copy;
+ BOOL rv = DuplicateHandle(GetCurrentProcess(), event_handle_, process,
+ &event_handle_copy, 0, FALSE, DUPLICATE_SAME_ACCESS);
+
+ if (rv)
+ *new_handle = event_handle_copy;
+ return rv ? true : false;
+}
+
+bool SharedEvent::GiveToProcess(ProcessHandle process,
+ SharedEventHandle *new_handle) {
+ bool rv = ShareToProcess(process, new_handle);
+ if (rv)
+ Close();
+ return rv;
+}
diff --git a/base/shared_event.h b/base/shared_event.h
new file mode 100644
index 0000000..2ddebc0
--- /dev/null
+++ b/base/shared_event.h
@@ -0,0 +1,87 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHARED_EVENT__
+#define BASE_SHARED_EVENT__
+
+#include "base/process_util.h"
+
+class TimeDelta;
+
+typedef HANDLE SharedEventHandle;
+
+class SharedEvent {
+ public:
+ // Create a new SharedEvent.
+ SharedEvent() : event_handle_(NULL) { }
+
+ // Create a SharedEvent from an existing SharedEventHandle. The new
+ // SharedEvent now owns the SharedEventHandle and will close it.
+ SharedEvent(SharedEventHandle event_handle) : event_handle_(event_handle) { }
+ ~SharedEvent();
+
+ // Create the SharedEvent.
+ bool Create(bool manual_reset, bool initial_state);
+
+ // Close the SharedEvent.
+ void Close();
+
+ // If |signaled| is true, set the signaled state, otherwise, set to nonsignaled.
+ // Returns false if we can't set the signaled state.
+ bool SetSignaledState(bool signaled);
+
+ // Returns true if the SharedEvent is signaled.
+ bool IsSignaled();
+
+ // Blocks until the event is signaled with a maximum wait time of |timeout|.
+ // Returns true if the object is signaled within the timeout.
+ bool WaitUntilSignaled(const TimeDelta& timeout);
+
+ // Blocks until the event is signaled. Returns true if the object is
+ // signaled, otherwise an error occurred.
+ bool WaitForeverUntilSignaled();
+
+ // Get access to the underlying OS handle for this event.
+ SharedEventHandle handle() { return event_handle_; }
+
+ // Share this SharedEvent with |process|. |new_handle| is an output
+ // parameter to receive the handle for use in |process|. Returns false if we
+ // are unable to share the SharedEvent.
+ bool ShareToProcess(ProcessHandle process, SharedEventHandle *new_handle);
+
+ // The same as ShareToProcess followed by closing the event.
+ bool GiveToProcess(ProcessHandle process, SharedEventHandle *new_handle);
+
+ private:
+ SharedEventHandle event_handle_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SharedEvent);
+};
+
+#endif // BASE_SHARED_EVENT__
diff --git a/base/shared_event_unittest.cc b/base/shared_event_unittest.cc
new file mode 100644
index 0000000..5a0c468
--- /dev/null
+++ b/base/shared_event_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h> // _beginthreadex
+
+#include "base/lock.h"
+#include "base/scoped_ptr.h"
+#include "base/shared_event.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SharedEventTest : public testing::Test {
+};
+
+Lock lock;
+
+unsigned __stdcall MultipleThreadMain(void* param) {
+ SharedEvent* shared_event = reinterpret_cast<SharedEvent*>(param);
+ AutoLock l(lock);
+ shared_event->SetSignaledState(!shared_event->IsSignaled());
+ return 0;
+}
+
+}
+
+TEST(SharedEventTest, ThreadSignaling) {
+ // Create a set of 5 threads to each open a shared event and flip the
+ // signaled state. Verify that when the threads complete, the final state is
+ // not-signaled.
+ // I admit this doesn't test much, but short of spawning separate processes
+ // and using IPC with a SharedEventHandle, there's not much to unittest.
+ const int kNumThreads = 5;
+ HANDLE threads[kNumThreads];
+
+ scoped_ptr<SharedEvent> shared_event(new SharedEvent);
+ shared_event->Create(true, true);
+
+ // Spawn the threads.
+ for (int16 index = 0; index < kNumThreads; index++) {
+ void *argument = reinterpret_cast<void*>(shared_event.get());
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id));
+ EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ CloseHandle(threads[index]);
+ }
+ EXPECT_FALSE(shared_event->IsSignaled());
+}
diff --git a/base/shared_memory.cc b/base/shared_memory.cc
new file mode 100644
index 0000000..bd5023f
--- /dev/null
+++ b/base/shared_memory.cc
@@ -0,0 +1,187 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/shared_memory.h"
+
+#include "base/logging.h"
+#include "base/win_util.h"
+
+SharedMemory::SharedMemory()
+ : mapped_file_(NULL),
+ memory_(NULL),
+ read_only_(false),
+ max_size_(0),
+ lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
+ : mapped_file_(handle),
+ memory_(NULL),
+ read_only_(read_only),
+ max_size_(0),
+ lock_(NULL) {
+}
+
+SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process)
+ : mapped_file_(NULL),
+ memory_(NULL),
+ read_only_(read_only),
+ max_size_(0),
+ lock_(NULL) {
+ ::DuplicateHandle(process, handle,
+ GetCurrentProcess(), &mapped_file_,
+ STANDARD_RIGHTS_REQUIRED |
+ (read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS),
+ FALSE, 0);
+}
+
+SharedMemory::~SharedMemory() {
+ Close();
+ if (lock_ != NULL)
+ CloseHandle(lock_);
+}
+
+bool SharedMemory::Create(const std::wstring &name, bool read_only,
+ bool open_existing, size_t size) {
+ DCHECK(mapped_file_ == NULL);
+
+ name_ = name;
+ read_only_ = read_only;
+ mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
+ read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, static_cast<DWORD>(size),
+ name.empty() ? NULL : name.c_str());
+ if (!mapped_file_)
+ return false;
+
+ // Check if the shared memory pre-exists.
+ if (GetLastError() == ERROR_ALREADY_EXISTS && !open_existing) {
+ Close();
+ return false;
+ }
+ max_size_ = size;
+ return true;
+}
+
+bool SharedMemory::Open(const std::wstring &name, bool read_only) {
+ DCHECK(mapped_file_ == NULL);
+
+ name_ = name;
+ read_only_ = read_only;
+ mapped_file_ = OpenFileMapping(
+ read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false,
+ name.empty() ? NULL : name.c_str());
+ if (mapped_file_ != NULL) {
+ // Note: size_ is not set in this case.
+ return true;
+ }
+ return false;
+}
+
+bool SharedMemory::Map(size_t bytes) {
+ if (mapped_file_ == NULL)
+ return false;
+
+ memory_ = MapViewOfFile(mapped_file_,
+ read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, 0, bytes);
+ if (memory_ != NULL) {
+ return true;
+ }
+ return false;
+}
+
+bool SharedMemory::Unmap() {
+ if (memory_ == NULL)
+ return false;
+
+ UnmapViewOfFile(memory_);
+ memory_ = NULL;
+ return true;
+}
+
+bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle,
+ bool close_self) {
+ *new_handle = 0;
+ DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ;
+ DWORD options = 0;
+ HANDLE mapped_file = mapped_file_;
+ HANDLE result;
+ if (!read_only_)
+ access |= FILE_MAP_WRITE;
+ if (close_self) {
+ // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
+ options = DUPLICATE_CLOSE_SOURCE;
+ mapped_file_ = NULL;
+ Unmap();
+ }
+
+ if (process == GetCurrentProcess() && close_self) {
+ *new_handle = mapped_file;
+ return true;
+ }
+
+ if (!DuplicateHandle(GetCurrentProcess(), mapped_file, process,
+ &result, access, FALSE, options))
+ return false;
+ *new_handle = result;
+ return true;
+}
+
+
+void SharedMemory::Close() {
+ if (memory_ != NULL) {
+ UnmapViewOfFile(memory_);
+ memory_ = NULL;
+ }
+
+ if (mapped_file_ != NULL) {
+ CloseHandle(mapped_file_);
+ mapped_file_ = NULL;
+ }
+}
+
+void SharedMemory::Lock() {
+ if (lock_ == NULL) {
+ std::wstring name = name_;
+ name.append(L"lock");
+ lock_ = CreateMutex(NULL, FALSE, name.c_str());
+ DCHECK(lock_ != NULL);
+ if (lock_ == NULL) {
+ DLOG(ERROR) << "Could not create mutex" << GetLastError();
+ return; // there is nothing good we can do here.
+ }
+ }
+ WaitForSingleObject(lock_, INFINITE);
+}
+
+void SharedMemory::Unlock() {
+ DCHECK(lock_ != NULL);
+ ReleaseMutex(lock_);
+}
diff --git a/base/shared_memory.h b/base/shared_memory.h
new file mode 100644
index 0000000..00b51f9
--- /dev/null
+++ b/base/shared_memory.h
@@ -0,0 +1,169 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SHARED_MEMORY_H__
+#define BASE_SHARED_MEMORY_H__
+
+#include "base/process_util.h"
+
+// SharedMemoryHandle is a platform specific type which represents
+// the underlying OS handle to a shared memory segment.
+#ifdef WIN32
+typedef HANDLE SharedMemoryHandle;
+typedef HANDLE SharedMemoryLock;
+#else
+typedef int SharedMemoryHandle;
+typedef int SharedMemoryLock;
+#endif
+
+// Platform abstraction for shared memory. Provides a C++ wrapper
+// around the OS primitive for a memory mapped file.
+class SharedMemory {
+ public:
+ // Create a new SharedMemory object.
+ SharedMemory();
+
+ // Create a new SharedMemory object from an existing, open
+ // shared memory file.
+ SharedMemory(SharedMemoryHandle handle, bool read_only);
+
+ // Create a new SharedMemory object from an existing, open
+ // shared memory file that was created by a remote process and not shared
+ // to the current process.
+ SharedMemory(SharedMemoryHandle handle, bool read_only,
+ ProcessHandle process);
+
+ // Destructor. Will close any open files.
+ ~SharedMemory();
+
+ // Creates or opens a shared memory segment based on a name.
+ // If read_only is true, opens the memory as read-only.
+ // If open_existing is true, and the shared memory already exists,
+ // opens the existing shared memory and ignores the size parameter.
+ // Returns true on success, false on failure.
+ bool Create(const std::wstring &name, bool read_only, bool open_existing,
+ size_t size);
+
+ // Opens a shared memory segment based on a name.
+ // If read_only is true, opens for read-only access.
+ // Returns true on success, false on failure.
+ bool Open(const std::wstring &name, bool read_only);
+
+ // Maps the shared memory into the caller's address space.
+ // Returns true on success, false otherwise. The memory address
+ // is accessed via the memory() accessor.
+ bool Map(size_t bytes);
+
+ // Unmaps the shared memory from the caller's address space.
+ // Returns true if successful; returns false on error or if the
+ // memory is not mapped.
+ bool Unmap();
+
+ // Get the size of the opened shared memory backing file.
+ // Note: This size is only available to the creator of the
+ // shared memory, and not to those that opened shared memory
+ // created externally.
+ // Returns 0 if not opened or unknown.
+ size_t max_size() const { return max_size_; }
+
+ // Gets a pointer to the opened memory space if it has been
+ // Mapped via Map(). Returns NULL if it is not mapped.
+ void *memory() const { return memory_; }
+
+ // Get access to the underlying OS handle for this segment.
+ // Use of this handle for anything other than an opaque
+ // identifier is not portable.
+ SharedMemoryHandle handle() const { return mapped_file_; }
+
+ // Closes the open shared memory segment.
+ // It is safe to call Close repeatedly.
+ void Close();
+
+ // Share the shared memory to another process. Attempts
+ // to create a platform-specific new_handle which can be
+ // used in a remote process to access the shared memory
+ // file. new_handle is an ouput parameter to receive
+ // the handle for use in the remote process.
+ // Returns true on success, false otherwise.
+ bool ShareToProcess(ProcessHandle process,
+ SharedMemoryHandle *new_handle) {
+ return ShareToProcessCommon(process, new_handle, false);
+ }
+
+ // Logically equivalent to:
+ // bool ok = ShareToProcess(process, new_handle);
+ // Close();
+ // return ok;
+ bool GiveToProcess(ProcessHandle process,
+ SharedMemoryHandle *new_handle) {
+ return ShareToProcessCommon(process, new_handle, true);
+ }
+
+ // Lock the shared memory.
+ // This is a cross-process lock which may be recursively
+ // locked by the same thread.
+ void Lock();
+
+ // Release the shared memory lock.
+ void Unlock();
+
+ private:
+ bool ShareToProcessCommon(ProcessHandle process,
+ SharedMemoryHandle *new_handle, bool close_self);
+
+ std::wstring name_;
+ SharedMemoryHandle mapped_file_;
+ void* memory_;
+ bool read_only_;
+ size_t max_size_;
+ SharedMemoryLock lock_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SharedMemory);
+};
+
+// A helper class that acquires the shared memory lock while
+// the SharedMemoryAutoLock is in scope.
+class SharedMemoryAutoLock {
+ public:
+ explicit SharedMemoryAutoLock(SharedMemory* shared_memory)
+ : shared_memory_(shared_memory) {
+ shared_memory_->Lock();
+ }
+
+ ~SharedMemoryAutoLock() {
+ shared_memory_->Unlock();
+ }
+
+ private:
+ SharedMemory* shared_memory_;
+ DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryAutoLock);
+};
+
+
+#endif // BASE_SHARED_MEMORY_H__
diff --git a/base/shared_memory_unittest.cc b/base/shared_memory_unittest.cc
new file mode 100644
index 0000000..928326a
--- /dev/null
+++ b/base/shared_memory_unittest.cc
@@ -0,0 +1,180 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h> // _beginthreadex
+#include "base/shared_memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+namespace {
+
+class SharedMemoryTest : public testing::Test {
+};
+
+unsigned __stdcall MultipleThreadMain(void* param) {
+ // Each thread will open the shared memory. Each thread will take
+ // a different 4 byte int pointer, and keep changing it, with some
+ // small pauses in between. Verify that each thread's value in the
+ // shared memory is always correct.
+ const int kDataSize = 1024;
+ std::wstring test_name = L"SharedMemoryOpenThreadTest";
+ int16 id = reinterpret_cast<int16>(param);
+ SharedMemory memory;
+ bool rv = memory.Create(test_name, false, true, kDataSize);
+ EXPECT_TRUE(rv);
+ rv = memory.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ int *ptr = static_cast<int*>(memory.memory()) + id;
+ EXPECT_EQ(*ptr, 0);
+ for (int idx = 0; idx < 100; idx++) {
+ *ptr = idx;
+ Sleep(1); // short wait
+ EXPECT_EQ(*ptr, idx);
+ }
+ memory.Close();
+ return 0;
+}
+
+unsigned __stdcall MultipleLockThread(void* param) {
+ // Each thread will open the shared memory. Each thread will take
+ // the memory, and keep changing it while trying to lock it, with some
+ // small pauses in between. Verify that each thread's value in the
+ // shared memory is always correct.
+ const int kDataSize = sizeof(int);
+ int id = static_cast<int>(reinterpret_cast<INT_PTR>(param));
+ SharedMemoryHandle handle = NULL;
+ {
+ SharedMemory memory1;
+ EXPECT_TRUE(memory1.Create(L"SharedMemoryMultipleLockThreadTest", false, true,
+ kDataSize));
+ EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle));
+ }
+ SharedMemory memory2(handle, false);
+ EXPECT_TRUE(memory2.Map(kDataSize));
+ volatile int* const ptr = static_cast<int*>(memory2.memory());
+ for (int idx = 0; idx < 20; idx++) {
+ memory2.Lock();
+ int i = (id << 16) + idx;
+ *ptr = i;
+ // short wait
+ Sleep(1);
+ EXPECT_EQ(*ptr, i);
+ memory2.Unlock();
+ }
+ memory2.Close();
+ return 0;
+}
+
+} // namespace
+
+TEST(SharedMemoryTest, OpenClose) {
+ const int kDataSize = 1024;
+ std::wstring test_name = L"SharedMemoryOpenCloseTest";
+
+ // Open two handles to a memory segment, confirm that they
+ // are mapped separately yet point to the same space.
+ SharedMemory memory1;
+ bool rv = memory1.Open(test_name, false);
+ EXPECT_FALSE(rv);
+ rv = memory1.Create(test_name, false, false, kDataSize);
+ EXPECT_TRUE(rv);
+ rv = memory1.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ SharedMemory memory2;
+ rv = memory2.Open(test_name, false);
+ EXPECT_TRUE(rv);
+ rv = memory2.Map(kDataSize);
+ EXPECT_TRUE(rv);
+ EXPECT_NE(memory1.memory(), memory2.memory()); // compare the pointers
+
+
+ // Write data to the first memory segment, verify contents of second.
+ memset(memory1.memory(), '1', kDataSize);
+ EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0);
+
+ // Close the first memory segment, and verify the
+ // second still has the right data.
+ memory1.Close();
+ char *start_ptr = static_cast<char *>(memory2.memory());
+ char *end_ptr = start_ptr + kDataSize;
+ for (char* ptr = start_ptr; ptr < end_ptr; ptr++)
+ EXPECT_EQ(*ptr, '1');
+
+ // Close the second memory segment
+ memory2.Close();
+}
+
+
+TEST(SharedMemoryTest, MultipleThreads) {
+ // Create a set of 5 threads to each open a shared memory segment
+ // and write to it. Verify that they are always reading/writing
+ // consistent data.
+ const int kNumThreads = 5;
+ HANDLE threads[kNumThreads];
+
+ // Spawn the threads.
+ for (int16 index = 0; index < kNumThreads; index++) {
+ void *argument = reinterpret_cast<void*>(index);
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, MultipleThreadMain, argument, 0, &thread_id));
+ EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ CloseHandle(threads[index]);
+ }
+}
+
+
+TEST(SharedMemoryTest, Lock) {
+ // Create a set of threads to each open a shared memory segment and write to
+ // it with the lock held. Verify that they are always reading/writing
+ // consistent data.
+ const int kNumThreads = 5;
+ HANDLE threads[kNumThreads];
+
+ // Spawn the threads.
+ for (int index = 0; index < kNumThreads; ++index) {
+ void *argument = reinterpret_cast<void*>(static_cast<INT_PTR>(index));
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, &MultipleLockThread, argument, 0, NULL));
+ EXPECT_NE(threads[index], static_cast<HANDLE>(NULL));
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; ++index) {
+ DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ CloseHandle(threads[index]);
+ }
+}
diff --git a/base/singleton.h b/base/singleton.h
new file mode 100644
index 0000000..47c0c1d
--- /dev/null
+++ b/base/singleton.h
@@ -0,0 +1,293 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_H__
+#define BASE_SINGLETON_H__
+
+#include <stdlib.h>
+
+#include <utility>
+
+#include "base/lock.h"
+#include "base/singleton_internal.h"
+
+#ifdef WIN32
+#include "base/fix_wp64.h"
+#else // WIN32
+#include <pthread.h>
+#endif // WIN32
+
+// Default traits for Singleton<Type>. Calls operator new and operator delete on
+// the object. Registers automatic deletion at library unload or process exit.
+// Overload if you need arguments or another memory allocation function.
+template<typename Type>
+struct DefaultSingletonTraits {
+ // Allocates the object.
+ static Type* New() {
+ // The parenthesis is very important here; it forces POD type
+ // initialization.
+ return new Type();
+ }
+
+ // Destroys the object.
+ static void Delete(Type* x) {
+ delete x;
+ }
+
+ // Set to true to automatically register deletion of the object on library
+ // unload or process exit.
+ static const bool kRegisterAtExit = true;
+
+ // Note: Only apply on Windows. Has *no effect* on other platform.
+ // When set to true, it signals that Trait::New() *must* not be called
+ // multiple times at construction. Anything that must be done to not enter
+ // this situation should be done at all cost. This simply involves creating a
+ // temporary lock.
+ static const bool kMustCallNewExactlyOnce = false;
+};
+
+
+// The Singleton<Type, Traits, DifferentiatingType> class manages a single
+// instance of Type which will be created on first use and will be destroyed at
+// library unload (or on normal process exit). The Trait::Delete function will
+// not be called on abnormal process exit.
+//
+// DifferentiatingType is used as a key to differentiate two different
+// singletons having the same memory allocation functions but serving a
+// different purpose. This is mainly used for Locks serving different purposes.
+//
+// Example usages: (none are preferred, they all result in the same code)
+// 1. FooClass* ptr = Singleton<FooClass>::get();
+// ptr->Bar();
+// 2. Singleton<FooClass>()->Bar();
+// 3. Singleton<FooClass>::get()->Bar();
+//
+// Singleton<> has no non-static members and doesn't need to actually be
+// instantiated. It does no harm to instantiate it and use it as a class member
+// or at global level since it is acting as a POD type.
+//
+// This class is itself thread-safe. The underlying Type must of course be
+// thread-safe if you want to use it concurrently. Two parameters may be tuned
+// depending on the user's requirements.
+//
+// Glossary:
+// MCNEO = kMustCallNewExactlyOnce
+// RAE = kRegisterAtExit
+//
+// On every platform, if Traits::RAE is true, the singleton will be destroyed at
+// library unload or process exit. if Traits::RAE is false, the singleton will
+// not be freed at library unload or process exit, thus the singleton will be
+// leaked if it is ever accessed. Traits::RAE shouldn't be false unless
+// absolutely necessary. Remember that the heap where the object is allocated
+// may be destroyed by the CRT anyway.
+//
+// On Windows, now the fun begins. Traits::New() may be called more than once
+// concurrently, but no user will gain access to the object until the winning
+// Traits::New() call is completed.
+//
+// On Windows, if Traits::MCNEO and Traits::RAE are both false,
+// Traits::Delete() can still be called. The reason is that a race condition can
+// occur during the object creation which will cause Traits::Delete() to be
+// called even if Traits::RAE is false, so Traits::Delete() should still be
+// implemented or objects may be leaked when there is a race condition in
+// creating the singleton. Even though this case is very rare, it may happen in
+// practice. To work around this situation, before creating a multithreaded
+// environment, be sure to call Singleton<>::get() to force the creation of the
+// instance.
+//
+// On Windows, If Traits::MCNEO is true, a temporary lock per singleton will be
+// created to ensure that Trait::New() is only called once.
+//
+// If you want to ensure that your class can only exist as a singleton, make
+// its constructors private, and make DefaultSingletonTraits<> a friend:
+//
+// #include "base/singleton.h"
+// class FooClass {
+// public:
+// void Bar() { ... }
+// private:
+// FooClass() { ... }
+// friend DefaultSingletonTraits<FooClass>;
+//
+// DISALLOW_EVIL_CONSTRUCTORS(FooClass);
+// };
+//
+// Caveats:
+// (a) Every call to get(), operator->() and operator*() incurs some overhead
+// (16ns on my P4/2.8GHz) to check whether the object has already been
+// initialized. You may wish to cache the result of get(); it will not
+// change.
+//
+// (b) Your factory function must never throw an exception. This class is not
+// exception-safe.
+//
+// (c) On Windows at least, if Traits::kMustCallNewExactlyOnce is false,
+// Traits::New() may be called two times in two different threads at the
+// same time so it must not have side effects. Set
+// Traits::kMustCallNewExactlyOnce to true to alleviate this issue, at
+// the cost of a slight increase of memory use and creation time.
+//
+template <typename Type,
+ typename Traits = DefaultSingletonTraits<Type>,
+ typename DifferentiatingType = Type>
+class Singleton
+ : public SingletonStorage<
+ Type,
+ std::pair<Traits, DifferentiatingType>,
+ UseVolatileSingleton<Traits::kMustCallNewExactlyOnce>::value> {
+ public:
+ // This class is safe to be constructed and copy-constructed since it has no
+ // member.
+
+ // Return a pointer to the one true instance of the class.
+ static Type* get() {
+ Type* value = instance_;
+ // Acute readers may think: why not just discard "value" and use
+ // "instance_" directly? Astute readers will remark that instance_ can be a
+ // volatile pointer on Windows and hence the compiler would be forced to
+ // generate two memory reads instead of just one. Since this is the hotspot,
+ // this is inefficient.
+ if (value)
+ return value;
+
+#ifdef WIN32
+ // Statically determine which function to call.
+ LockedConstruct<Traits::kMustCallNewExactlyOnce>();
+#else // WIN32
+ // Posix platforms already have the functionality embedded.
+ pthread_once(&control_, SafeConstruct);
+#endif // WIN32
+ return instance_;
+ }
+
+ // Shortcuts.
+ Type& operator*() {
+ return *get();
+ }
+
+ Type* operator->() {
+ return get();
+ }
+
+ private:
+#ifdef WIN32
+ // Use bool template differentiation to make sure to not build the other part
+ // of the code. We don't want to instantiate Singleton<Lock, ...> uselessly.
+ template<bool kUseLock>
+ static void LockedConstruct() {
+ // Define a differentiating type for the Lock.
+ typedef std::pair<Type, std::pair<Traits, DifferentiatingType> >
+ LockDifferentiatingType;
+
+ // Object-type lock. Note that the lock singleton is different per singleton
+ // type.
+ AutoLock lock(*Singleton<Lock,
+ DefaultSingletonTraits<Lock>,
+ LockDifferentiatingType>());
+ // Now that we have the lock, look if the instance is created, if not yet,
+ // create it.
+ if (!instance_)
+ SafeConstruct();
+ }
+
+ template<>
+ static void LockedConstruct<false>() {
+ // Implemented using atomic compare-and-swap. The new object is
+ // constructed and used as the new value in the operation; if the
+ // compare fails, the new object will be deleted. Future implementations
+ // for Windows might use InitOnceExecuteOnce (Vista-only), similar in
+ // spirit to pthread_once.
+
+ // On Windows, multiple concurrent Traits::New() calls are tolerated.
+ Type* value = Traits::New();
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<void* volatile*>(&instance_), value, NULL)) {
+ // Race condition, discard the temporary value.
+ Traits::Delete(value);
+ } else {
+ // Got it, register destruction at unload. atexit() is called on library
+ // unload. It is assumed that atexit() is itself thread safe. It is also
+ // assumed that registered functions by atexit are called in a thread
+ // safe manner. At least on Windows, they are called with the loader
+ // lock held. On Windows, the CRT use a structure similar to
+ // std::map<dll_handle,std::vector<registered_functions>> so the right
+ // functions are called on library unload, independent of having a DLL
+ // CRT or a static CRT or even both.
+ if (Traits::kRegisterAtExit)
+ atexit(&OnExit);
+ }
+ }
+#endif // WIN32
+
+ // SafeConstruct is guaranteed to be executed only once.
+ static void SafeConstruct() {
+ instance_ = Traits::New();
+
+ // Porting note: this code depends on some properties of atexit which are
+ // not guaranteed by the standard:
+ // - atexit must be thread-safe: its internal manipulation of the list of
+ // registered functions must be tolerant of multiple threads attempting
+ // to register exit routines simultaneously.
+ // - exit routines must run when the executable module that contains them
+ // is unloaded. For routines in by dynamically-loaded modules, this
+ // may be sooner than process termination.
+ // - atexit should support an arbitrary number of registered exit
+ // routines, or at least should support more routines than will
+ // actually be registered (the standard only requires 32).
+ // The atexit implementations in contemporary versions of Mac OS X, glibc,
+ // and the Windows C runtime provide these capabilities. To port to other
+ // systems with less-advanced (even though still standard-conforming)
+ // atexit implmentations, consider alternatives such as __cxa_atexit or
+ // custom termination sections.
+ if (Traits::kRegisterAtExit)
+ atexit(OnExit);
+ }
+
+ // Adapter function for use with atexit().
+ static void OnExit() {
+ if (!instance_)
+ return;
+ Traits::Delete(instance_);
+ instance_ = NULL;
+ }
+
+#ifndef WIN32
+ static pthread_once_t control_;
+#endif // !WIN32
+};
+
+#ifndef WIN32
+
+template <typename Type, typename Traits, typename DifferentiatingType>
+pthread_once_t Singleton<Type, Traits, DifferentiatingType>::control_ =
+ PTHREAD_ONCE_INIT;
+
+#endif // !WIN32
+
+#endif // BASE_SINGLETON_H__
diff --git a/base/singleton_dll_unittest.cc b/base/singleton_dll_unittest.cc
new file mode 100644
index 0000000..c663db3
--- /dev/null
+++ b/base/singleton_dll_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/singleton_dll_unittest.h"
+#include "base/logging.h"
+
+BOOL APIENTRY DllMain(HMODULE module, DWORD reason_for_call, LPVOID reserved) {
+ switch (reason_for_call) {
+ case DLL_PROCESS_ATTACH:
+ DisableThreadLibraryCalls(module);
+ break;
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
+COMPILE_ASSERT(DefaultSingletonTraits<int>::kRegisterAtExit == true, a);
+COMPILE_ASSERT(
+ DefaultSingletonTraits<int>::kMustCallNewExactlyOnce == false,
+ b);
+
+template<typename Type>
+struct LockTrait : public DefaultSingletonTraits<Type> {
+ static const bool kMustCallNewExactlyOnce = true;
+};
+
+struct Init5Trait : public DefaultSingletonTraits<int> {
+ static int* New() {
+ return new int(5);
+ }
+};
+
+struct CallbackTrait : public CustomAllocTrait<CallBackFunc> {
+ static void Delete(CallBackFunc* p) {
+ if (*p)
+ (*p)();
+ CHECK(CustomAllocTrait<CallBackFunc>::Delete(p));
+ }
+};
+
+struct NoLeakTrait : public CallbackTrait {
+};
+
+struct LeakTrait : public CallbackTrait {
+ static const bool kRegisterAtExit = false;
+};
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt1() {
+ return Singleton<int>::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt2() {
+ // Force to use a different singleton than SingletonInt1.
+ return Singleton<int, DefaultSingletonTraits<int> >::get();
+}
+
+class DummyDifferentiatingClass {
+};
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt3() {
+ // Force to use a different singleton than SingletonInt1 and SingletonInt2.
+ // Note that any type can be used; int, float, std::wstring...
+ return Singleton<int, DefaultSingletonTraits<int>,
+ DummyDifferentiatingClass>::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt4() {
+ return Singleton<int, LockTrait<int> >::get();
+}
+
+SINGLETON_UNITTEST_API int* WINAPI SingletonInt5() {
+ return Singleton<int, Init5Trait>::get();
+}
+
+SINGLETON_UNITTEST_API void WINAPI SingletonNoLeak(CallBackFunc CallOnQuit) {
+ *Singleton<CallBackFunc, NoLeakTrait>::get() = CallOnQuit;
+}
+
+SINGLETON_UNITTEST_API void WINAPI SingletonLeak(CallBackFunc CallOnQuit) {
+ *Singleton<CallBackFunc, LeakTrait>::get() = CallOnQuit;
+}
+
+SINGLETON_UNITTEST_API CallBackFunc* WINAPI GetLeakySingleton() {
+ return Singleton<CallBackFunc, LeakTrait>::get();
+}
diff --git a/base/singleton_dll_unittest.h b/base/singleton_dll_unittest.h
new file mode 100644
index 0000000..d161722
--- /dev/null
+++ b/base/singleton_dll_unittest.h
@@ -0,0 +1,83 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_DLL_UNITTEST_H__
+#define BASE_SINGLETON_DLL_UNITTEST_H__
+
+#include "base/singleton.h"
+
+#ifdef SINGLETON_UNITTEST_EXPORTS
+#define SINGLETON_UNITTEST_API __declspec(dllexport)
+#else
+#define SINGLETON_UNITTEST_API __declspec(dllimport)
+#endif
+
+// Function pointer for singleton getters.
+typedef int* (WINAPI* SingletonIntFunc)();
+
+// Callback function to be called on library unload.
+typedef void (WINAPI* CallBackFunc)();
+
+// Leaky/nonleak singleton initialization.
+typedef void (WINAPI* LeakySingletonFunc)(CallBackFunc);
+
+// Retrieve the leaky singleton for later disposal.
+typedef CallBackFunc* (WINAPI* GetLeakySingletonFunc)();
+
+// When using new/delete, the heap is destroyed on library unload. So use
+// VirtualAlloc/VirtualFree to bypass this behavior.
+template<typename Type>
+struct CustomAllocTrait : public DefaultSingletonTraits<Type> {
+ static Type* New() {
+ return static_cast<Type*>(VirtualAlloc(NULL, sizeof(Type), MEM_COMMIT,
+ PAGE_READWRITE));
+ }
+
+ static bool Delete(Type* p) {
+ return 0!=VirtualFree(p, 0, MEM_RELEASE);
+ }
+};
+
+// 1 and 2 share the same instance.
+// 3 simply use a different key.
+// 4 sets kMustCallNewExactlyOnce to true.
+// 5 default initialize to 5.
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt1();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt2();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt3();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt4();
+extern "C" SINGLETON_UNITTEST_API int* WINAPI SingletonInt5();
+
+extern "C" SINGLETON_UNITTEST_API void WINAPI SingletonNoLeak(
+ CallBackFunc CallOnQuit);
+extern "C" SINGLETON_UNITTEST_API void WINAPI SingletonLeak(
+ CallBackFunc CallOnQuit);
+extern "C" SINGLETON_UNITTEST_API CallBackFunc* WINAPI GetLeakySingleton();
+
+#endif // BASE_SINGLETON_DLL_UNITTEST_H__
diff --git a/base/singleton_internal.h b/base/singleton_internal.h
new file mode 100644
index 0000000..82fce94
--- /dev/null
+++ b/base/singleton_internal.h
@@ -0,0 +1,66 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_SINGLETON_INTERNAL_H__
+#define BASE_SINGLETON_INTERNAL_H__
+
+// Define the storage of the singleton pointer.
+template <typename Type, typename DifferentiatingType, bool kVolatile>
+class SingletonStorage {
+ protected:
+ // The actual pointer. Note that it is a volatile pointer.
+ static Type* volatile instance_;
+};
+
+// Partial specialization.
+template <typename Type, typename DifferentiatingType>
+class SingletonStorage<Type, DifferentiatingType, false> {
+ protected:
+ // The pointer does not need to be volatile for use with pthread_once or
+ // locked initialization.
+ static Type* instance_;
+};
+
+template <typename Type, typename DifferentiatingType, bool kVolatile>
+Type* volatile SingletonStorage<Type, DifferentiatingType,
+ kVolatile>::instance_ = NULL;
+
+template <typename Type, typename DifferentiatingType>
+Type* SingletonStorage<Type, DifferentiatingType, false>::instance_ = NULL;
+
+template<bool kUseVolatile>
+struct UseVolatileSingleton {
+#ifdef WIN32
+ static const bool value = kUseVolatile;
+#else
+ static const bool value = false;
+#endif // WIN32
+};
+
+#endif // BASE_SINGLETON_INTERNAL_H__
diff --git a/base/singleton_unittest.cc b/base/singleton_unittest.cc
new file mode 100644
index 0000000..5d21593
--- /dev/null
+++ b/base/singleton_unittest.cc
@@ -0,0 +1,227 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/singleton_dll_unittest.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+
+class SingletonTest : public testing::Test {
+ public:
+ SingletonTest() {
+ }
+
+ virtual void SetUp() {
+ module_ = NULL;
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ }
+
+ virtual void TearDown() {
+ ASSERT_FALSE(module_);
+ }
+
+ static bool IsTestCaseDisabled() {
+ // Check if the dll exists beside the executable.
+ std::wstring path;
+ PathService::Get(base::DIR_EXE, &path);
+ file_util::AppendToPath(&path, kLibrary);
+ return !file_util::PathExists(path);
+ }
+
+ protected:
+ void LoadLibrary() {
+ ASSERT_FALSE(module_);
+ module_ = ::LoadLibrary(kLibrary);
+ ASSERT_TRUE(module_ != NULL);
+ }
+
+ void FreeLibrary() {
+ ASSERT_TRUE(module_ != NULL);
+ ASSERT_TRUE(::FreeLibrary(module_));
+ module_ = NULL;
+ }
+
+ template<typename T>
+ void GetProc(const char* function_name, T* function) {
+ ASSERT_TRUE(module_ != NULL);
+ *function = reinterpret_cast<T>(GetProcAddress(module_, function_name));
+ ASSERT_TRUE(*function);
+ }
+
+ void VerifiesCallbacks() {
+ EXPECT_TRUE(non_leak_called_);
+ EXPECT_FALSE(leaky_called_);
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ }
+
+ void VerifiesCallbacksNotCalled() {
+ EXPECT_FALSE(non_leak_called_);
+ EXPECT_FALSE(leaky_called_);
+ non_leak_called_ = false;
+ leaky_called_ = false;
+ }
+
+ static void WINAPI CallbackNoLeak() {
+ non_leak_called_ = true;
+ }
+
+ static void WINAPI CallbackLeak() {
+ leaky_called_ = true;
+ }
+
+ private:
+ static const wchar_t* const kLibrary;
+ HMODULE module_;
+ static bool non_leak_called_;
+ static bool leaky_called_;
+};
+
+bool SingletonTest::non_leak_called_ = false;
+bool SingletonTest::leaky_called_ = false;
+
+const wchar_t* const SingletonTest::kLibrary = L"singleton_dll_unittest.dll";
+
+TEST_F(SingletonTest, Basic) {
+ if (IsTestCaseDisabled())
+ return;
+
+ int* singleton_int_1;
+ int* singleton_int_2;
+ int* singleton_int_3;
+ int* singleton_int_4;
+ int* singleton_int_5;
+ CallBackFunc* leaky_singleton;
+
+ LoadLibrary();
+ {
+ SingletonIntFunc sut1;
+ SingletonIntFunc sut2;
+ SingletonIntFunc sut3;
+ SingletonIntFunc sut4;
+ SingletonIntFunc sut5;
+ {
+ GetProc("SingletonInt1", &sut1);
+ singleton_int_1 = sut1();
+ }
+ // Ensure POD type initialization.
+ EXPECT_EQ(*singleton_int_1, 0);
+ *singleton_int_1 = 1;
+
+ EXPECT_EQ(singleton_int_1, sut1());
+ EXPECT_EQ(*singleton_int_1, 1);
+
+ {
+ GetProc("SingletonInt2", &sut2);
+ singleton_int_2 = sut2();
+ }
+ // Same instance that 1.
+ EXPECT_EQ(*singleton_int_2, 1);
+ EXPECT_EQ(singleton_int_1, singleton_int_2);
+
+ {
+ GetProc("SingletonInt3", &sut3);
+ singleton_int_3 = sut3();
+ }
+ // Different instance than 1 and 2.
+ EXPECT_EQ(*singleton_int_3, 0);
+ EXPECT_NE(singleton_int_1, singleton_int_3);
+ *singleton_int_3 = 3;
+ EXPECT_EQ(*singleton_int_1, 1);
+ EXPECT_EQ(*singleton_int_2, 1);
+
+ {
+ GetProc("SingletonInt4", &sut4);
+ singleton_int_4 = sut4();
+ }
+ // Use a lock for creation. Not really tested at length.
+ EXPECT_EQ(*singleton_int_4, 0);
+ *singleton_int_4 = 4;
+ EXPECT_NE(singleton_int_1, singleton_int_4);
+ EXPECT_NE(singleton_int_3, singleton_int_4);
+
+ {
+ GetProc("SingletonInt5", &sut5);
+ singleton_int_5 = sut5();
+ }
+ // Is default initialized to 5.
+ EXPECT_EQ(*singleton_int_5, 5);
+ EXPECT_NE(singleton_int_1, singleton_int_5);
+ EXPECT_NE(singleton_int_3, singleton_int_5);
+ EXPECT_NE(singleton_int_4, singleton_int_5);
+#ifdef _DEBUG
+ // In release, the optimizer may make both exports use exactly the same
+ // code.
+ EXPECT_NE(sut1, sut2);
+#endif
+ EXPECT_NE(sut2, sut3);
+ EXPECT_NE(sut3, sut4);
+ EXPECT_NE(sut4, sut5);
+
+ LeakySingletonFunc noleak;
+ GetProc("SingletonNoLeak", &noleak);
+ noleak(&CallbackNoLeak);
+ LeakySingletonFunc leak;
+ GetProc("SingletonLeak", &leak);
+ leak(&CallbackLeak);
+ GetLeakySingletonFunc get_leaky;
+ GetProc("GetLeakySingleton", &get_leaky);
+ leaky_singleton = get_leaky();
+ EXPECT_TRUE(leaky_singleton);
+ }
+ FreeLibrary();
+
+ // Verify that only the expected callback has been called.
+ VerifiesCallbacks();
+ // Delete the leaky singleton. It is interesting to note that Purify does
+ // *not* detect the leak when this call is commented out. :(
+ EXPECT_TRUE(CustomAllocTrait<CallBackFunc>::Delete(leaky_singleton));
+
+ LoadLibrary();
+ {
+ // Verifiy that the variables were reset.
+ {
+ SingletonIntFunc sut1;
+ GetProc("SingletonInt1", &sut1);
+ singleton_int_1 = sut1();
+ EXPECT_EQ(*singleton_int_1, 0);
+ }
+ {
+ SingletonIntFunc sut5;
+ GetProc("SingletonInt5", &sut5);
+ singleton_int_5 = sut5();
+ EXPECT_EQ(*singleton_int_5, 5);
+ }
+ }
+ // The leaky singleton shouldn't leak since SingletonLeak has not been called.
+ FreeLibrary();
+
+ VerifiesCallbacksNotCalled();
+}
diff --git a/base/spin_wait.h b/base/spin_wait.h
new file mode 100644
index 0000000..7226387
--- /dev/null
+++ b/base/spin_wait.h
@@ -0,0 +1,74 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file provides a macro ONLY for use in testing.
+// DO NOT USE IN PRODUCTION CODE. There are much better ways to wait.
+
+// This code is very helpful in testing multi-threaded code, without depending
+// on almost any primitives. This is especially helpful if you are testing
+// those primitive multi-threaded constructs.
+
+// We provide a simple one argument spin wait (for 1 second), and a generic
+// spin wait (for longer periods of time).
+
+#ifndef BASE_SPIN_WAIT_H__
+#define BASE_SPIN_WAIT_H__
+
+#include "base/time.h"
+
+// Provide a macro that will wait no longer than 1 second for an asynchronous
+// change is the value of an expression.
+// A typical use would be:
+//
+// SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 == f(x));
+//
+// The expression will be evaluated repeatedly until it is true, or until
+// the time (1 second) expires.
+// Since tests generally have a 5 second watch dog timer, this spin loop is
+// typically used to get the padding needed on a given test platform to assure
+// that the test passes, even if load varies, and external events vary.
+
+#define SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(expression) \
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1), (expression))
+
+#define SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(delta, expression) do { \
+ Time start = Time::Now(); \
+ const TimeDelta kTimeout = delta; \
+ while(!(expression)) { \
+ if (kTimeout < Time::Now() - start) { \
+ EXPECT_LE((Time::Now() - start).InMilliseconds(), \
+ kTimeout.InMilliseconds()) << "Timed out"; \
+ break; \
+ } \
+ Sleep(50); \
+ } \
+ } \
+ while(0)
+
+#endif // BASE_SPIN_WAIT_H__
diff --git a/base/stack_container.h b/base/stack_container.h
new file mode 100644
index 0000000..16c6ab7
--- /dev/null
+++ b/base/stack_container.h
@@ -0,0 +1,255 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STACK_CONTAINER_H__
+#define BASE_STACK_CONTAINER_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+// This allocator can be used with STL containers to provide a stack buffer
+// from which to allocate memory and overflows onto the heap. This stack buffer
+// would be allocated on the stack and allows us to avoid heap operations in
+// some situations.
+//
+// STL likes to make copies of allocators, so the allocator itself can't hold
+// the data. Instead, we make the creator responsible for creating a
+// StackAllocator::Source which contains the data. Copying the allocator
+// merely copies the pointer to this shared source, so all allocators created
+// based on our allocator will share the same stack buffer.
+//
+// This stack buffer implementation is very simple. The first allocation that
+// fits in the stack buffer will use the stack buffer. Any subsequent
+// allocations will not use the stack buffer, even if there is unused room.
+// This makes it appropriate for array-like containers, but the caller should
+// be sure to reserve() in the container up to the stack buffer size. Otherwise
+// the container will allocate a small array which will "use up" the stack
+// buffer.
+template<typename T, size_t stack_capacity>
+class StackAllocator : public std::allocator<T> {
+ public:
+ // Backing store for the allocator. The container owner is responsible for
+ // maintaining this for as long as any containers using this allocator are
+ // live.
+ struct Source {
+ Source() : used_stack_buffer_(false) {
+ }
+
+ // Casts the buffer in its right type.
+ T* stack_buffer() { return reinterpret_cast<T*>(stack_buffer_); }
+ const T* stack_buffer() const {
+ return reinterpret_cast<const T*>(stack_buffer_);
+ }
+
+ //
+ // IMPORTANT: Take care to ensure that stack_buffer_ is aligned
+ // since it is used to mimic an array of T.
+ // Be careful while declaring any unaligned types (like bool)
+ // before stack_buffer_.
+ //
+
+ // The buffer itself. It is not of type T because we don't want the
+ // constructors and destructors to be automatically called. Define a POD
+ // buffer of the right size instead.
+ char stack_buffer_[sizeof(T[stack_capacity])];
+
+ // Set when the stack buffer is used for an allocation. We do not track
+ // how much of the buffer is used, only that somebody is using it.
+ bool used_stack_buffer_;
+ };
+
+ // Used by containers when they want to refer to an allocator of type U.
+ template<typename U>
+ struct rebind {
+ typedef StackAllocator<U, stack_capacity> other;
+ };
+
+ StackAllocator(Source* source) : source_(source) {
+ }
+ StackAllocator(const StackAllocator& other) : source_(other.source_) {
+ }
+
+ // Actually do the allocation. Use the stack buffer if nobody has used it yet
+ // and the size requested fits. Otherwise, fall through to the standard
+ // allocator.
+ pointer allocate(size_type n, void* hint = 0) {
+ if (!source_->used_stack_buffer_ && n <= stack_capacity) {
+ source_->used_stack_buffer_ = true;
+ return source_->stack_buffer();
+ } else {
+ return std::allocator<T>::allocate(n, hint);
+ }
+ }
+
+ // Free: when trying to free the stack buffer, just mark it as free. For
+ // non-stack-buffer pointers, just fall though to the standard allocator.
+ void deallocate(pointer p, size_type n) {
+ if (p == source_->stack_buffer())
+ source_->used_stack_buffer_ = false;
+ else
+ std::allocator<T>::deallocate(p, n);
+ }
+
+ private:
+ Source* source_;
+};
+
+// A wrapper around STL containers that maintains a stack-sized buffer that the
+// initial capacity of the vector is based on. Growing the container beyond the
+// stack capacity will transparently overflow onto the heap. The container must
+// support reserve().
+//
+// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this
+// type. This object is really intended to be used only internally. You'll want
+// to use the wrappers below for different types.
+template<typename ContainerType, int stack_capacity>
+class StackContainer {
+ public:
+ typedef typename ContainerType ContainerType;
+ typedef typename ContainerType::value_type ContainedType;
+ typedef StackAllocator<ContainedType, stack_capacity> Allocator;
+
+ // Allocator must be constructed before the container!
+ StackContainer() : allocator_(&stack_data_), container_(allocator_) {
+ // Make the container use the stack allocation by reserving our buffer size
+ // before doing anything else.
+ container_.reserve(stack_capacity);
+ }
+
+ // Getters for the actual container.
+ //
+ // Danger: any copies of this made using the copy constructor must have
+ // shorter lifetimes than the source. The copy will share the same allocator
+ // and therefore the same stack buffer as the original. Use std::copy to
+ // copy into a "real" container for longer-lived objects.
+ ContainerType& container() { return container_; }
+ const ContainerType& container() const { return container_; }
+
+ // Support operator-> to get to the container. This allows nicer syntax like:
+ // StackContainer<...> foo;
+ // std::sort(foo->begin(), foo->end());
+ ContainerType* operator->() { return &container_; }
+ const ContainerType* operator->() const { return &container_; }
+
+#ifdef UNIT_TEST
+ // Retrieves the stack source so that that unit tests can verify that the
+ // buffer is being used properly.
+ typename const Allocator::Source& stack_data() const {
+ return stack_data_;
+ }
+#endif
+
+ protected:
+ typename Allocator::Source stack_data_;
+ Allocator allocator_;
+ ContainerType container_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(StackContainer);
+};
+
+// StackString
+template<size_t stack_capacity>
+class StackString : public StackContainer<
+ std::basic_string<char,
+ std::char_traits<char>,
+ StackAllocator<char, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackString() : StackContainer<
+ std::basic_string<char,
+ std::char_traits<char>,
+ StackAllocator<char, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(StackString);
+};
+
+// StackWString
+template<size_t stack_capacity>
+class StackWString : public StackContainer<
+ std::basic_string<wchar_t,
+ std::char_traits<wchar_t>,
+ StackAllocator<wchar_t, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackWString() : StackContainer<
+ std::basic_string<wchar_t,
+ std::char_traits<wchar_t>,
+ StackAllocator<wchar_t, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(StackWString);
+};
+
+// StackVector
+//
+// Example:
+// StackVector<int, 16> foo;
+// foo->push_back(22); // we have overloaded operator->
+// foo[0] = 10; // as well as operator[]
+template<typename T, size_t stack_capacity>
+class StackVector : public StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity> {
+ public:
+ StackVector() : StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity>() {
+ }
+
+ // We need to put this in STL containers sometimes, which requires a copy
+ // constructor. We can't call the regular copy constructor because that will
+ // take the stack buffer from the original. Here, we create an empty object
+ // and make a stack buffer of its own.
+ StackVector(const StackVector<T, stack_capacity>& other)
+ : StackContainer<
+ std::vector<T, StackAllocator<T, stack_capacity> >,
+ stack_capacity>() {
+ container().assign(other->begin(), other->end());
+ }
+
+ StackVector<T, stack_capacity>& operator=(
+ const StackVector<T, stack_capacity>& other) {
+ container().assign(other->begin(), other->end());
+ return *this;
+ }
+
+ // Vectors are commonly indexed, which isn't very convenient even with
+ // operator-> (using "->at()" does exception stuff we don't want).
+ T& operator[](size_t i) { return container().operator[](i); }
+ const T& operator[](size_t i) const { return container().operator[](i); }
+};
+
+#endif // BASE_STACK_CONTAINER_H__
diff --git a/base/stack_container_unittest.cc b/base/stack_container_unittest.cc
new file mode 100644
index 0000000..aa8f34a
--- /dev/null
+++ b/base/stack_container_unittest.cc
@@ -0,0 +1,137 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/stack_container.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/ref_counted.h"
+
+namespace {
+
+class Dummy : public base::RefCounted<Dummy> {
+ public:
+ Dummy(int* alive) : alive_(alive) {
+ ++*alive_;
+ }
+ ~Dummy() {
+ --*alive_;
+ }
+ private:
+ int* const alive_;
+};
+
+} // namespace
+
+TEST(StackContainer, Vector) {
+ const int stack_size = 3;
+ StackVector<int, stack_size> vect;
+ const int* stack_buffer = &vect.stack_data().stack_buffer()[0];
+
+ // The initial |stack_size| elements should appear in the stack buffer.
+ EXPECT_EQ(stack_size, vect.container().capacity());
+ for (int i = 0; i < stack_size; i++) {
+ vect.container().push_back(i);
+ EXPECT_EQ(stack_buffer, &vect.container()[0]);
+ EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+ }
+
+ // Adding more elements should push the array onto the heap.
+ for (int i = 0; i < stack_size; i++) {
+ vect.container().push_back(i + stack_size);
+ EXPECT_NE(stack_buffer, &vect.container()[0]);
+ EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+ }
+
+ // The array should still be in order.
+ for (int i = 0; i < stack_size * 2; i++)
+ EXPECT_EQ(i, vect.container()[i]);
+
+ // Resize to smaller. Our STL implementation won't reallocate in this case,
+ // otherwise it might use our stack buffer. We reserve right after the resize
+ // to guarantee it isn't using the stack buffer, even though it doesn't have
+ // much data.
+ vect.container().resize(stack_size);
+ vect.container().reserve(stack_size * 2);
+ EXPECT_FALSE(vect.stack_data().used_stack_buffer_);
+
+ // Copying the small vector to another should use the same allocator and use
+ // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since
+ // they have to get the template types just right and it can cause errors.
+ std::vector<int, StackAllocator<int, stack_size> > other(vect.container());
+ EXPECT_EQ(stack_buffer, &other.front());
+ EXPECT_TRUE(vect.stack_data().used_stack_buffer_);
+ for (int i = 0; i < stack_size; i++)
+ EXPECT_EQ(i, other[i]);
+}
+
+TEST(StackContainer, VectorDoubleDelete) {
+ // Regression testing for double-delete.
+ typedef StackVector<scoped_refptr<Dummy>, 2> Vector;
+ typedef Vector::ContainerType Container;
+ Vector vect;
+
+ int alive = 0;
+ scoped_refptr<Dummy> dummy(new Dummy(&alive));
+ EXPECT_EQ(alive, 1);
+
+ vect->push_back(dummy);
+ EXPECT_EQ(alive, 1);
+
+ Dummy* dummy_unref = dummy.get();
+ dummy = NULL;
+ EXPECT_EQ(alive, 1);
+
+ Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref);
+ EXPECT_EQ(itr->get(), dummy_unref);
+ vect->erase(itr);
+ EXPECT_EQ(alive, 0);
+
+ // Shouldn't crash at exit.
+}
+
+TEST(StackContainer, BufferAlignment) {
+ StackVector<wchar_t, 16> text;
+ text->push_back(L'A');
+ text->push_back(L'B');
+ text->push_back(L'C');
+ text->push_back(L'D');
+ text->push_back(L'E');
+ text->push_back(L'F');
+ text->push_back(0);
+
+ const wchar_t* buffer = &text[1];
+ bool even_aligned = (0 == (((size_t)buffer) & 0x1));
+ EXPECT_EQ(even_aligned, true);
+}
+
+// Make sure all the class compiles correctly.
+template StackVector<int, 2>;
+template StackVector<scoped_refptr<Dummy>, 2>;
diff --git a/base/stats_counters.h b/base/stats_counters.h
new file mode 100644
index 0000000..778b824
--- /dev/null
+++ b/base/stats_counters.h
@@ -0,0 +1,297 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+#ifndef BASE_STATS_COUNTERS_H__
+#define BASE_STATS_COUNTERS_H__
+
+#include <string>
+#include "base/stats_table.h"
+#include "base/time.h"
+
+// StatsCounters are dynamically created values which can be tracked in
+// the StatsTable. They are designed to be lightweight to create and
+// easy to use.
+//
+// Since StatsCounters can be created dynamically by name, there is
+// a hash table lookup to find the counter in the table. A StatsCounter
+// object can be created once and used across multiple threads safely.
+//
+// Example usage:
+// {
+// StatsCounter request_count("RequestCount");
+// request_count.Increment();
+// }
+//
+// Note that creating counters on the stack does work, however creating
+// the counter object requires a hash table lookup. For inner loops, it
+// may be better to create the counter either as a member of another object
+// (or otherwise outside of the loop) for maximum performance.
+//
+// Internally, a counter represents a value in a row of a StatsTable.
+// The row has a 32bit value for each process/thread in the table and also
+// a name (stored in the table metadata).
+//
+// NOTE: In order to make stats_counters usable in lots of different code,
+// avoid any dependencies inside this header file.
+//
+
+//------------------------------------------------------------------------------
+// Define macros for ease of use. They also allow us to change definitions
+// as the implementation varies, or depending on compile options.
+//------------------------------------------------------------------------------
+// First provide generic macros, which exist in production as well as debug.
+#define STATS_COUNTER(name, delta) do { \
+ static StatsCounter counter(name); \
+ counter.Add(delta); \
+} while (0)
+
+#define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1)
+
+#define RATE_COUNTER(name, duration) do { \
+ static StatsRate hit_count(name); \
+ hit_count.AddTime(duration); \
+} while (0)
+
+// Define Debug vs non-debug flavors of macros.
+#ifndef NDEBUG
+
+#define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta)
+#define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name)
+#define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration)
+
+#else // NDEBUG
+
+#define DSTATS_COUNTER(name, delta) do {} while (0)
+#define DSIMPLE_STATS_COUNTER(name) do {} while (0)
+#define DRATE_COUNTER(name, duration) do {} while (0)
+
+#endif // NDEBUG
+
+//------------------------------------------------------------------------------
+// StatsCounter represents a counter in the StatsTable class.
+class StatsCounter {
+ public:
+ // Create a StatsCounter object.
+ explicit StatsCounter(const std::wstring& name)
+ : counter_id_(-1) {
+ // We prepend the name with 'c:' to indicate that it is a counter.
+ name_ = L"c:";
+ name_.append(name);
+ };
+
+ virtual ~StatsCounter() {}
+
+ // Sets the counter to a specific value.
+ void Set(int value) {
+ int* loc = GetPtr();
+ if (loc) *loc = value;
+ }
+
+ // Increments the counter.
+ void Increment() {
+ Add(1);
+ }
+
+ // TODO(jar) temporary hack include method till base and chrome use new name.
+ void Increment(int value) {
+ Add(value);
+ }
+
+ virtual void Add(int value) {
+ int* loc = GetPtr();
+ if (loc)
+ (*loc) += value;
+ }
+
+ // Decrements the counter.
+ void Decrement() {
+ Add(-1);
+ }
+
+ void Subtract(int value) {
+ Add(-value);
+ }
+
+ // TODO(jar) temporary hack includes method till base and chrome use new name.
+ void Decrement(int value) {
+ Add(-value);
+ }
+
+ // Is this counter enabled?
+ // Returns false if table is full.
+ bool Enabled() {
+ return GetPtr() != NULL;
+ }
+
+ int value() {
+ int* loc = GetPtr();
+ if (loc) return *loc;
+ return 0;
+ }
+
+ protected:
+ StatsCounter()
+ : counter_id_(-1) {
+ }
+
+ // Returns the cached address of this counter location.
+ int* GetPtr() {
+ StatsTable* table = StatsTable::current();
+ if (!table)
+ return NULL;
+
+ // If counter_id_ is -1, then we haven't looked it up yet.
+ if (counter_id_ == -1) {
+ counter_id_ = table->FindCounter(name_);
+ if (table->GetSlot() == 0) {
+ if (!table->RegisterThread(L"")) {
+ // There is no room for this thread. This thread
+ // cannot use counters.
+ counter_id_ = 0;
+ return NULL;
+ }
+ }
+ }
+
+ // If counter_id_ is > 0, then we have a valid counter.
+ if (counter_id_ > 0)
+ return table->GetLocation(counter_id_, table->GetSlot());
+
+ // counter_id_ was zero, which means the table is full.
+ return NULL;
+ }
+
+ std::wstring name_;
+ // The counter id in the table. We initialize to -1 (an invalid value)
+ // and then cache it once it has been looked up. The counter_id is
+ // valid across all threads and processes.
+ int32 counter_id_;
+};
+
+
+// A StatsCounterTimer is a StatsCounter which keeps a timer during
+// the scope of the StatsCounterTimer. On destruction, it will record
+// its time measurement.
+class StatsCounterTimer : protected StatsCounter {
+ public:
+ // Constructs and starts the timer.
+ explicit StatsCounterTimer(const std::wstring& name) {
+ // we prepend the name with 't:' to indicate that it is a timer.
+ name_ = L"t:";
+ name_.append(name);
+ }
+
+ // Start the timer.
+ void Start() {
+ if (!Enabled())
+ return;
+ start_time_ = TimeTicks::Now();
+ stop_time_ = TimeTicks();
+ }
+
+ // Stop the timer and record the results.
+ void Stop() {
+ if (!Enabled() || !Running())
+ return;
+ stop_time_ = TimeTicks::Now();
+ Record();
+ }
+
+ // Returns true if the timer is running.
+ bool Running() {
+ return Enabled() && !start_time_.is_null() && stop_time_.is_null();
+ }
+
+ // Accept a TimeDelta to increment.
+ virtual void AddTime(TimeDelta time) {
+ Add(static_cast<int>(time.InMilliseconds()));
+ }
+
+ // TODO(jar) temporary hack include method till base and chrome use new name.
+ void IncrementTimer(TimeDelta time) {
+ AddTime(time);
+ }
+
+ protected:
+ // Compute the delta between start and stop, in milliseconds.
+ void Record() {
+ AddTime(stop_time_ - start_time_);
+ }
+
+ TimeTicks start_time_;
+ TimeTicks stop_time_;
+};
+
+// A StatsRate is a timer that keeps a count of the number of intervals added so
+// that several statistics can be produced:
+// min, max, avg, count, total
+class StatsRate : public StatsCounterTimer {
+ public:
+ // Constructs and starts the timer.
+ explicit StatsRate(const wchar_t* name)
+ : StatsCounterTimer(name),
+ counter_(name),
+ largest_add_(std::wstring(L" ").append(name).append(L"MAX").c_str()) {
+ }
+
+ virtual void Add(int value) {
+ counter_.Increment();
+ StatsCounterTimer::Add(value);
+ if (value > largest_add_.value())
+ largest_add_.Set(value);
+ }
+
+ private:
+ StatsCounter counter_;
+ StatsCounter largest_add_;
+};
+
+
+// Helper class for scoping a timer or rate.
+template<class T> class StatsScope {
+ public:
+ explicit StatsScope<T>(T& timer)
+ : timer_(timer) {
+ timer_.Start();
+ }
+
+ ~StatsScope() {
+ timer_.Stop();
+ }
+
+ void Stop() {
+ timer_.Stop();
+ }
+
+ private:
+ T& timer_;
+};
+
+#endif // BASE_STATS_COUNTERS_H__
diff --git a/base/stats_table.cc b/base/stats_table.cc
new file mode 100644
index 0000000..8213c34
--- /dev/null
+++ b/base/stats_table.cc
@@ -0,0 +1,545 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/stats_table.h"
+
+#include "base/logging.h"
+#include "base/thread_local_storage.h"
+
+// The StatsTable uses a shared memory segment that is laid out as follows
+//
+// +-------------------------------------------+
+// | Version | Size | MaxCounters | MaxThreads |
+// +-------------------------------------------+
+// | Thread names table |
+// +-------------------------------------------+
+// | Thread TID table |
+// +-------------------------------------------+
+// | Thread PID table |
+// +-------------------------------------------+
+// | Counter names table |
+// +-------------------------------------------+
+// | Data |
+// +-------------------------------------------+
+//
+// The data layout is a grid, where the columns are the thread_ids and the
+// rows are the counter_ids.
+//
+// If the first character of the thread_name is '\0', then that column is
+// empty.
+// If the first character of the counter_name is '\0', then that row is
+// empty.
+//
+// About Locking:
+// This class is designed to be both multi-thread and multi-process safe.
+// Aside from initialization, this is done by partitioning the data which
+// each thread uses so that no locking is required. However, to allocate
+// the rows and columns of the table to particular threads, locking is
+// required.
+//
+// At the shared-memory level, we have a lock. This lock protects the
+// shared-memory table only, and is used when we create new counters (e.g.
+// use rows) or when we register new threads (e.g. use columns). Reading
+// data from the table does not require any locking at the shared memory
+// level.
+//
+// Each process which accesses the table will create a StatsTable object.
+// The StatsTable maintains a hash table of the existing counters in the
+// table for faster lookup. Since the hash table is process specific,
+// each process maintains its own cache. We avoid complexity here by never
+// de-allocating from the hash table. (Counters are dynamically added,
+// but not dynamically removed).
+
+// In order for external viewers to be able to read our shared memory,
+// we all need to use the same size ints.
+COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints);
+
+namespace {
+
+// An internal version in case we ever change the format of this
+// file, and so that we can identify our table.
+const int kTableVersion = 0x13131313;
+
+// The name for un-named counters and threads in the table.
+const wchar_t kUnknownName[] = L"<unknown>";
+
+// Various header information contained in the memory mapped segment.
+struct TableHeader {
+ int version;
+ int size;
+ int max_counters;
+ int max_threads;
+};
+
+// Calculates delta to align an offset to the size of an int
+inline int AlignOffset(int offset) {
+ return (sizeof(int) - (offset % sizeof(int))) % sizeof(int);
+}
+
+inline int AlignedSize(int size) {
+ return size + AlignOffset(size);
+}
+
+// StatsTableTLSData carries the data stored in the TLS slots for the
+// StatsTable. This is used so that we can properly cleanup when the
+// thread exits and return the table slot.
+//
+// Each thread that calls RegisterThread in the StatsTable will have
+// a StatsTableTLSData stored in its TLS.
+struct StatsTableTLSData {
+ StatsTable* table;
+ int slot;
+};
+
+// The SlotReturnFunction is called at thread exit for each thread
+// which used the StatsTable.
+static void SlotReturnFunction(void* data) {
+ StatsTableTLSData* tls_data = static_cast<StatsTableTLSData*>(data);
+ if (tls_data) {
+ DCHECK(tls_data->table);
+ tls_data->table->UnregisterThread();
+ }
+}
+
+} // namespace
+
+// The StatsTablePrivate maintains convenience pointers into the
+// shared memory segment. Use this class to keep the data structure
+// clean and accessible.
+class StatsTablePrivate {
+ public:
+ // Create the StatsTablePrivate based on expected size parameters.
+ StatsTablePrivate(void* memory, int size, int max_threads, int max_counters);
+
+ // Accessors for our header pointers
+ TableHeader* table_header() const { return table_header_; }
+ int version() const { return table_header_->version; }
+ int size() const { return table_header_->size; }
+ int max_counters() const { return table_header_->max_counters; }
+ int max_threads() const { return table_header_->max_threads; }
+
+ // Accessors for our tables
+ wchar_t* thread_name(int slot_id) const {
+ return &thread_names_table_[
+ (slot_id-1) * (StatsTable::kMaxThreadNameLength)];
+ }
+ int* thread_tid(int slot_id) const {
+ return &(thread_tid_table_[slot_id-1]);
+ }
+ int* thread_pid(int slot_id) const {
+ return &(thread_pid_table_[slot_id-1]);
+ }
+ wchar_t* counter_name(int counter_id) const {
+ return &counter_names_table_[
+ (counter_id-1) * (StatsTable::kMaxCounterNameLength)];
+ }
+ int* row(int counter_id) const {
+ return &data_table_[(counter_id-1) * max_threads()];
+ }
+
+ private:
+ // Initializes the table on first access. Sets header values
+ // appropriately and zeroes all counters.
+ void InitializeTable(void* memory, int size, int max_counters,
+ int max_threads);
+
+ // Initializes our in-memory pointers into a pre-created StatsTable.
+ void ComputeMappedPointers(void* memory);
+
+ TableHeader* table_header_;
+ wchar_t* thread_names_table_;
+ int* thread_tid_table_;
+ int* thread_pid_table_;
+ wchar_t* counter_names_table_;
+ int* data_table_;
+};
+
+StatsTablePrivate::StatsTablePrivate(void* memory, int size, int max_threads,
+ int max_counters) {
+ TableHeader* header = static_cast<TableHeader*>(memory);
+ // If the version does not match, then assume the table needs
+ // to be initialized.
+ if (header->version != kTableVersion)
+ InitializeTable(memory, size, max_counters, max_threads);
+
+ // We have a valid table, so compute our pointers.
+ ComputeMappedPointers(memory);
+}
+
+void StatsTablePrivate::InitializeTable(void* memory, int size,
+ int max_counters,
+ int max_threads) {
+ // Zero everything.
+ memset(memory, 0, size);
+
+ // Initialize the header.
+ TableHeader* header = static_cast<TableHeader*>(memory);
+ header->version = kTableVersion;
+ header->size = size;
+ header->max_counters = max_counters;
+ header->max_threads = max_threads;
+}
+
+void StatsTablePrivate::ComputeMappedPointers(void* memory) {
+ char* data = static_cast<char*>(memory);
+ int offset = 0;
+
+ table_header_ = reinterpret_cast<TableHeader*>(data);
+ offset += sizeof(*table_header_);
+ offset += AlignOffset(offset);
+
+ // Verify we're looking at a valid StatsTable.
+ DCHECK_EQ(table_header_->version, kTableVersion);
+
+ thread_names_table_ = reinterpret_cast<wchar_t*>(data + offset);
+ offset += sizeof(wchar_t) *
+ max_threads() * StatsTable::kMaxThreadNameLength;
+ offset += AlignOffset(offset);
+
+ thread_tid_table_ = reinterpret_cast<int*>(data + offset);
+ offset += sizeof(int) * max_threads();
+ offset += AlignOffset(offset);
+
+ thread_pid_table_ = reinterpret_cast<int*>(data + offset);
+ offset += sizeof(int) * max_threads();
+ offset += AlignOffset(offset);
+
+ counter_names_table_ = reinterpret_cast<wchar_t*>(data + offset);
+ offset += sizeof(wchar_t) *
+ max_counters() * StatsTable::kMaxCounterNameLength;
+ offset += AlignOffset(offset);
+
+ data_table_ = reinterpret_cast<int*>(data + offset);
+ offset += sizeof(int) * max_threads() * max_counters();
+
+ DCHECK_EQ(offset, size());
+}
+
+
+
+// We keep a singleton table which can be easily accessed.
+StatsTable* StatsTable::global_table_ = NULL;
+
+StatsTable::StatsTable(const std::wstring& name, int max_threads,
+ int max_counters)
+ : tls_index_(ThreadLocalStorage::Alloc(SlotReturnFunction)) {
+ int table_size =
+ AlignedSize(sizeof(TableHeader)) +
+ AlignedSize((max_counters * sizeof(wchar_t) * kMaxCounterNameLength)) +
+ AlignedSize((max_threads * sizeof(wchar_t) * kMaxThreadNameLength)) +
+ AlignedSize(max_threads * sizeof(int)) +
+ AlignedSize(max_threads * sizeof(int)) +
+ AlignedSize((sizeof(int) * (max_counters * max_threads)));
+
+ impl_ = NULL;
+ // TODO(mbelshe): Move this out of the constructor
+ if (shared_memory_.Create(name, false, true, table_size))
+ if (shared_memory_.Map(table_size))
+ impl_ = new StatsTablePrivate(shared_memory_.memory(), table_size,
+ max_threads, max_counters);
+ if (!impl_)
+ LOG(ERROR) << "StatsTable did not initialize:" << GetLastError();
+}
+
+StatsTable::~StatsTable() {
+ // Before we tear down our copy of the table, be sure to
+ // unregister our thread.
+ UnregisterThread();
+
+ // Return ThreadLocalStorage. At this point, if any registered threads
+ // still exist, they cannot Unregister.
+ ThreadLocalStorage::Free(tls_index_);
+
+ // Cleanup our shared memory.
+ delete impl_;
+
+ // If we are the global table, unregister ourselves.
+ if (global_table_ == this)
+ global_table_ = NULL;
+}
+
+int StatsTable::RegisterThread(const std::wstring& name) {
+ int slot = 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(&shared_memory_);
+ slot = FindEmptyThread();
+ if (!slot) {
+ return 0;
+ }
+
+ DCHECK(impl_);
+
+ // We have space, so consume a column in the table.
+ std::wstring thread_name = name;
+ if (name.empty())
+ thread_name = kUnknownName;
+ wcsncpy_s(impl_->thread_name(slot), kMaxThreadNameLength,
+ thread_name.c_str(), _TRUNCATE);
+ *(impl_->thread_tid(slot)) = GetCurrentThreadId();
+ *(impl_->thread_pid(slot)) = GetCurrentProcessId();
+ }
+
+ // Set our thread local storage.
+ StatsTableTLSData* data = new StatsTableTLSData;
+ data->table = this;
+ data->slot = slot;
+ ThreadLocalStorage::Set(tls_index_, data);
+ return slot;
+}
+
+StatsTableTLSData* StatsTable::GetTLSData() const {
+ StatsTableTLSData* data =
+ static_cast<StatsTableTLSData*>(ThreadLocalStorage::Get(tls_index_));
+ if (!data)
+ return NULL;
+
+ DCHECK(data->slot);
+ DCHECK_EQ(data->table, this);
+ return data;
+}
+
+void StatsTable::UnregisterThread() {
+ StatsTableTLSData* data = GetTLSData();
+ if (!data)
+ return;
+ DCHECK(impl_);
+
+ // Mark the slot free by zeroing out the thread name.
+ wchar_t* name = impl_->thread_name(data->slot);
+ *name = L'\0';
+
+ // Remove the calling thread's TLS so that it cannot use the slot.
+ ThreadLocalStorage::Set(tls_index_, NULL);
+ delete data;
+}
+
+int StatsTable::CountThreadsRegistered() const {
+ if (!impl_)
+ return 0;
+
+ // Loop through the shared memory and count the threads that are active.
+ // We intentionally do not lock the table during the operation.
+ int count = 0;
+ for (int index = 1; index <= impl_->max_threads(); index++) {
+ wchar_t* name = impl_->thread_name(index);
+ if (*name != L'\0')
+ count++;
+ }
+ return count;
+}
+
+int StatsTable::GetSlot() const {
+ StatsTableTLSData* data = GetTLSData();
+ if (!data)
+ return 0;
+ return data->slot;
+}
+
+int StatsTable::FindEmptyThread() const {
+ // Note: the API returns slots numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ //
+ // The reason for doing this is because the thread 'slot' is stored
+ // in TLS, which is always initialized to zero, not -1. If 0 were
+ // returned as a valid slot number, it would be confused with the
+ // uninitialized state.
+ if (!impl_)
+ return 0;
+
+ int index = 1;
+ for (; index <= impl_->max_threads(); index++) {
+ wchar_t* name = impl_->thread_name(index);
+ if (!*name)
+ break;
+ }
+ if (index > impl_->max_threads())
+ return 0; // The table is full.
+ return index;
+}
+
+int StatsTable::FindCounterOrEmptyRow(const std::wstring& name) const {
+ // Note: the API returns slots numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ //
+ // There isn't much reason for this other than to be consistent
+ // with the way we track columns for thread slots. (See comments
+ // in FindEmptyThread for why it is done this way).
+ if (!impl_)
+ return 0;
+
+ int free_slot = 0;
+ for (int index = 1; index <= impl_->max_counters(); index++) {
+ wchar_t* row_name = impl_->counter_name(index);
+ if (!*row_name && !free_slot)
+ free_slot = index; // save that we found a free slot
+ else if (!wcsncmp(row_name, name.c_str(), kMaxCounterNameLength))
+ return index;
+ }
+ return free_slot;
+}
+
+int StatsTable::FindCounter(const std::wstring& name) {
+ // Note: the API returns counters numbered from 1..N, although
+ // internally, the array is 0..N-1. This is so that we can return
+ // zero as "not found".
+ if (!impl_)
+ return 0;
+
+ // Create a scope for our auto-lock.
+ {
+ AutoLock scoped_lock(counters_lock_);
+
+ // Attempt to find the counter.
+ CountersMap::const_iterator iter;
+ iter = counters_.find(name);
+ if (iter != counters_.end())
+ return iter->second;
+ }
+
+ // Counter does not exist, so add it.
+ return AddCounter(name);
+}
+
+int StatsTable::AddCounter(const std::wstring& name) {
+ DCHECK(impl_);
+
+ if (!impl_)
+ return 0;
+
+ int counter_id = 0;
+ {
+ // To add a counter to the shared memory, we need the
+ // shared memory lock.
+ SharedMemoryAutoLock lock(&shared_memory_);
+
+ // We have space, so create a new counter.
+ counter_id = FindCounterOrEmptyRow(name);
+ if (!counter_id)
+ return 0;
+
+ std::wstring counter_name = name;
+ if (name.empty())
+ counter_name = kUnknownName;
+ wcsncpy_s(impl_->counter_name(counter_id), kMaxCounterNameLength,
+ counter_name.c_str(), _TRUNCATE);
+ }
+
+ // now add to our in-memory cache
+ {
+ AutoLock lock(counters_lock_);
+ counters_[name] = counter_id;
+ }
+ return counter_id;
+}
+
+int* StatsTable::GetLocation(int counter_id, int slot_id) const {
+ if (!impl_)
+ return NULL;
+ if (slot_id > impl_->max_threads())
+ return NULL;
+
+ int* row = impl_->row(counter_id);
+ return &(row[slot_id-1]);
+}
+
+const wchar_t* StatsTable::GetRowName(int index) const {
+ if (!impl_)
+ return NULL;
+
+ return impl_->counter_name(index);
+}
+
+int StatsTable::GetRowValue(int index, int pid) const {
+ if (!impl_)
+ return 0;
+
+ int rv = 0;
+ int* row = impl_->row(index);
+ for (int index = 0; index < impl_->max_threads(); index++) {
+ if (pid == 0 || *impl_->thread_pid(index) == pid)
+ rv += row[index];
+ }
+ return rv;
+}
+
+int StatsTable::GetRowValue(int index) const {
+ return GetRowValue(index, 0);
+}
+
+int StatsTable::GetCounterValue(const std::wstring& name, int pid) {
+ if (!impl_)
+ return 0;
+
+ int row = FindCounter(name);
+ if (!row)
+ return 0;
+ return GetRowValue(row, pid);
+}
+
+int StatsTable::GetCounterValue(const std::wstring& name) {
+ return GetCounterValue(name, 0);
+}
+
+int StatsTable::GetMaxCounters() const {
+ if (!impl_)
+ return 0;
+ return impl_->max_counters();
+}
+
+int StatsTable::GetMaxThreads() const {
+ if (!impl_)
+ return 0;
+ return impl_->max_threads();
+}
+
+int* StatsTable::FindLocation(const wchar_t* name) {
+ // Get the static StatsTable
+ StatsTable *table = StatsTable::current();
+ if (!table)
+ return NULL;
+
+ // Get the slot for this thread. Try to register
+ // it if none exists.
+ int slot = table->GetSlot();
+ if (!slot && !(slot = table->RegisterThread(L"")))
+ return NULL;
+
+ // Find the counter id for the counter.
+ std::wstring str_name(name);
+ int counter = table->FindCounter(str_name);
+
+ // Now we can find the location in the table.
+ return table->GetLocation(counter, slot);
+}
diff --git a/base/stats_table.h b/base/stats_table.h
new file mode 100644
index 0000000..0938099
--- /dev/null
+++ b/base/stats_table.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// A StatsTable is a table of statistics. It can be used across multiple
+// processes and threads, maintaining cheap statistics counters without
+// locking.
+//
+// The goal is to make it very cheap and easy for developers to add
+// counters to code, without having to build one-off utilities or mechanisms
+// to track the counters, and also to allow a single "view" to display
+// the contents of all counters.
+//
+// To achieve this, StatsTable creates a shared memory segment to store
+// the data for the counters. Upon creation, it has a specific size
+// which governs the maximum number of counters and concurrent
+// threads/processes which can use it.
+//
+
+#ifndef BASE_STATS_TABLE_H__
+#define BASE_STATS_TABLE_H__
+
+#include <string>
+#include "base/basictypes.h"
+#include "base/hash_tables.h"
+#include "base/lock.h"
+#include "base/shared_memory.h"
+#include "base/thread_local_storage.h"
+
+class StatsTablePrivate;
+
+namespace {
+struct StatsTableTLSData;
+}
+
+class StatsTable {
+ public:
+ // Create a new StatsTable.
+ // If a StatsTable already exists with the specified name, this StatsTable
+ // will use the same shared memory segment as the original. Otherwise,
+ // a new StatsTable is created and all counters are zeroed.
+ //
+ // name is the name of the StatsTable to use.
+ //
+ // max_threads is the maximum number of threads the table will support.
+ // If the StatsTable already exists, this number is ignored.
+ //
+ // max_counters is the maximum number of counters the table will support.
+ // If the StatsTable already exists, this number is ignored.
+ StatsTable(const std::wstring& name, int max_threads, int max_counters);
+
+ // Destroys the StatsTable. When the last StatsTable is destroyed
+ // (across all processes), the StatsTable is removed from disk.
+ ~StatsTable();
+
+ // For convenience, we create a static table. This is generally
+ // used automatically by the counters.
+ static StatsTable* current() { return global_table_; }
+
+ // Set the global table for use in this process.
+ static void set_current(StatsTable* value) { global_table_ = value; }
+
+ // Get the slot id for the calling thread. Returns 0 if no
+ // slot is assigned.
+ int GetSlot() const;
+
+ // All threads that contribute data to the table must register with the
+ // table first. This function will set thread local storage for the
+ // thread containing the location in the table where this thread will
+ // write its counter data.
+ //
+ // name is just a debugging tag to label the thread, and it does not
+ // need to be unique. It will be truncated to kMaxThreadNameLength-1
+ // characters.
+ //
+ // On success, returns the slot id for this thread. On failure,
+ // returns 0.
+ int RegisterThread(const std::wstring& name);
+
+ // Returns the space occupied by a thread in the table. Generally used
+ // if a thread terminates but the process continues. This function
+ // does not zero out the thread's counters.
+ void UnregisterThread();
+
+ // Returns the number of threads currently registered. This is really not
+ // useful except for diagnostics and debugging.
+ int CountThreadsRegistered() const;
+
+ // Find a counter in the StatsTable.
+ //
+ // Returns an id for the counter which can be used to call GetLocation().
+ // If the counter does not exist, attempts to create a row for the new
+ // counter. If there is no space in the table for the new counter,
+ // returns 0.
+ int FindCounter(const std::wstring& name);
+
+ // TODO(mbelshe): implement RemoveCounter.
+
+ // Gets the location of a particular value in the table based on
+ // the counter id and slot id.
+ int* GetLocation(int counter_id, int slot_id) const;
+
+ // Gets the counter name at a particular row. If the row is empty,
+ // returns NULL.
+ const wchar_t* GetRowName(int index) const;
+
+ // Gets the sum of the values for a particular row.
+ int GetRowValue(int index) const;
+
+ // Gets the sum of the values for a particular row for a given pid.
+ int GetRowValue(int index, int pid) const;
+
+ // Gets the sum of the values for a particular counter. If the counter
+ // does not exist, creates the counter.
+ int GetCounterValue(const std::wstring& name);
+
+ // Gets the sum of the values for a particular counter for a given pid.
+ // If the counter does not exist, creates the counter.
+ int GetCounterValue(const std::wstring& name, int pid);
+
+ // The maxinum number of counters/rows in the table.
+ int GetMaxCounters() const;
+
+ // The maxinum number of threads/columns in the table.
+ int GetMaxThreads() const;
+
+ // The maximum length (in characters) of a Thread's name including
+ // null terminator, as stored in the shared memory.
+ static const int kMaxThreadNameLength = 32;
+
+ // The maximum length (in characters) of a Counter's name including
+ // null terminator, as stored in the shared memory.
+ static const int kMaxCounterNameLength = 32;
+
+ // Convenience function to lookup a counter location for a
+ // counter by name for the calling thread. Will register
+ // the thread if it is not already registered.
+ static int* FindLocation(const wchar_t *name);
+
+ private:
+ // Locates a free slot in the table. Returns a number > 0 on success,
+ // or 0 on failure. The caller must hold the shared_memory lock when
+ // calling this function.
+ int FindEmptyThread() const;
+
+ // Locates a counter in the table or finds an empty row. Returns a
+ // number > 0 on success, or 0 on failure. The caller must hold the
+ // shared_memory_lock when calling this function.
+ int FindCounterOrEmptyRow(const std::wstring& name) const;
+
+ // Internal function to add a counter to the StatsTable. Assumes that
+ // the counter does not already exist in the table.
+ //
+ // name is a unique identifier for this counter, and will be truncated
+ // to kMaxCounterNameLength-1 characters.
+ //
+ // On success, returns the counter_id for the newly added counter.
+ // On failure, returns 0.
+ int AddCounter(const std::wstring& name);
+
+ // Get the TLS data for the calling thread. Returns NULL if none is
+ // initialized.
+ StatsTableTLSData* GetTLSData() const;
+
+ typedef base::hash_map<std::wstring, int> CountersMap;
+
+ bool opened_;
+ SharedMemory shared_memory_;
+ StatsTablePrivate* impl_;
+ // The counters_lock_ protects the counters_ hash table.
+ Lock counters_lock_;
+ // The counters_ hash map is an in-memory hash of the counters.
+ // It is used for quick lookup of counters, but is cannot be used
+ // as a substitute for what is in the shared memory. Even though
+ // we don't have a counter in our hash table, another process may
+ // have created it.
+ CountersMap counters_;
+ TLSSlot tls_index_;
+
+ static StatsTable* global_table_;
+ DISALLOW_EVIL_CONSTRUCTORS(StatsTable);
+};
+
+#endif // BASE_STATS_TABLE_H__
diff --git a/base/stats_table_unittest.cc b/base/stats_table_unittest.cc
new file mode 100644
index 0000000..28527fc
--- /dev/null
+++ b/base/stats_table_unittest.cc
@@ -0,0 +1,396 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <windows.h>
+
+#include "base/multiprocess_test.h"
+#include "base/stats_table.h"
+#include "base/stats_counters.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class StatsTableTest : public MultiProcessTest {
+ };
+}
+
+// Open a StatsTable and verify that we can write to each of the
+// locations in the table.
+TEST_F(StatsTableTest, VerifySlots) {
+ const std::wstring kTableName = L"VerifySlotsStatTable";
+ const int kMaxThreads = 1;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+
+ // Register a single thread.
+ std::wstring thread_name = L"mainThread";
+ int slot_id = table.RegisterThread(thread_name);
+ EXPECT_TRUE(slot_id);
+
+ // Fill up the table with counters.
+ std::wstring counter_base_name = L"counter";
+ for (int index=0; index < kMaxCounter; index++) {
+ std::wstring counter_name = counter_base_name;
+ StringAppendF(&counter_name, L"counter.ctr%d", index);
+ int counter_id = table.FindCounter(counter_name);
+ EXPECT_GT(counter_id, 0);
+ }
+
+ // Try to allocate an additional thread. Verify it fails.
+ slot_id = table.RegisterThread(L"too many threads");
+ EXPECT_EQ(slot_id, 0);
+
+ // Try to allocate an additional counter. Verify it fails.
+ int counter_id = table.FindCounter(counter_base_name);
+ EXPECT_EQ(counter_id, 0);
+}
+
+// CounterZero will continually be set to 0.
+const std::wstring kCounterZero = L"CounterZero";
+// Counter1313 will continually be set to 1313.
+const std::wstring kCounter1313 = L"Counter1313";
+// CounterIncrement will be incremented each time.
+const std::wstring kCounterIncrement = L"CounterIncrement";
+// CounterDecrement will be decremented each time.
+const std::wstring kCounterDecrement = L"CounterDecrement";
+// CounterMixed will be incremented by odd numbered threads and
+// decremented by even threads.
+const std::wstring kCounterMixed = L"CounterMixed";
+// The number of thread loops that we will do.
+const int kThreadLoops = 1000;
+
+unsigned __stdcall StatsTableMultipleThreadMain(void* param) {
+ // Each thread will open the shared memory and set counters
+ // concurrently in a loop. We'll use some pauses to
+ // mixup the thread scheduling.
+ int16 id = reinterpret_cast<int16>(param);
+
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ for (int index = 0; index < kThreadLoops; index++) {
+ StatsCounter mixed_counter(kCounterMixed); // create this one in the loop
+ zero_counter.Set(0);
+ lucky13_counter.Set(1313);
+ increment_counter.Increment();
+ decrement_counter.Decrement();
+ if (id % 2)
+ mixed_counter.Decrement();
+ else
+ mixed_counter.Increment();
+ Sleep(index % 10); // short wait
+ }
+ return 0;
+}
+// Create a few threads and have them poke on their counters.
+TEST_F(StatsTableTest, MultipleThreads) {
+ // Create a stats table.
+ const std::wstring kTableName = L"MultipleThreadStatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ // Spin up a set of threads to go bang on the various counters.
+ // After we join the threads, we'll make sure the counters
+ // contain the values we expected.
+ HANDLE threads[kMaxThreads];
+
+ // Spawn the threads.
+ for (int16 index = 0; index < kMaxThreads; index++) {
+ void* argument = reinterpret_cast<void*>(index);
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, StatsTableMultipleThreadMain, argument, 0,
+ &thread_id));
+ EXPECT_NE((HANDLE)NULL, threads[index]);
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kMaxThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ }
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ StatsCounter mixed_counter(kCounterMixed);
+
+ // Verify the various counters are correct.
+ std::wstring name;
+ name = L"c:" + kCounterZero;
+ EXPECT_EQ(0, table.GetCounterValue(name));
+ name = L"c:" + kCounter1313;
+ EXPECT_EQ(1313 * kMaxThreads,
+ table.GetCounterValue(name));
+ name = L"c:" + kCounterIncrement;
+ EXPECT_EQ(kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ name = L"c:" + kCounterDecrement;
+ EXPECT_EQ(-kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ name = L"c:" + kCounterMixed;
+ EXPECT_EQ((kMaxThreads % 2) * kThreadLoops,
+ table.GetCounterValue(name));
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+}
+
+const std::wstring kTableName = L"MultipleProcessStatTable";
+
+extern "C" int __declspec(dllexport) ChildProcessMain() {
+ // Each process will open the shared memory and set counters
+ // concurrently in a loop. We'll use some pauses to
+ // mixup the scheduling.
+
+ StatsTable table(kTableName, 0, 0);
+ StatsTable::set_current(&table);
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+ for (int index = 0; index < kThreadLoops; index++) {
+ zero_counter.Set(0);
+ lucky13_counter.Set(1313);
+ increment_counter.Increment();
+ decrement_counter.Decrement();
+ Sleep(index % 10); // short wait
+ }
+ return 0;
+}
+
+// Create a few threads and have them poke on their counters.
+TEST_F(StatsTableTest, MultipleProcesses) {
+ // Create a stats table.
+ const std::wstring kTableName = L"MultipleProcessStatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+
+ // Spin up a set of threads to go bang on the various counters.
+ // After we join the threads, we'll make sure the counters
+ // contain the values we expected.
+ HANDLE threads[kMaxThreads];
+
+ // Spawn the processes.
+ for (int16 index = 0; index < kMaxThreads; index++) {
+ threads[index] = this->SpawnChild(L"ChildProcessMain");
+ EXPECT_NE((HANDLE)NULL, threads[index]);
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kMaxThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], 60 * 1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+ }
+ StatsCounter zero_counter(kCounterZero);
+ StatsCounter lucky13_counter(kCounter1313);
+ StatsCounter increment_counter(kCounterIncrement);
+ StatsCounter decrement_counter(kCounterDecrement);
+
+ // Verify the various counters are correct.
+ std::wstring name;
+ name = L"c:" + kCounterZero;
+ EXPECT_EQ(0, table.GetCounterValue(name));
+ name = L"c:" + kCounter1313;
+ EXPECT_EQ(1313 * kMaxThreads,
+ table.GetCounterValue(name));
+ name = L"c:" + kCounterIncrement;
+ EXPECT_EQ(kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ name = L"c:" + kCounterDecrement;
+ EXPECT_EQ(-kMaxThreads * kThreadLoops,
+ table.GetCounterValue(name));
+ EXPECT_EQ(0, table.CountThreadsRegistered());
+}
+
+class MockStatsCounter : public StatsCounter {
+ public:
+ MockStatsCounter(const std::wstring& name)
+ : StatsCounter(name) {}
+ int* Pointer() { return GetPtr(); }
+};
+
+// Test some basic StatsCounter operations
+TEST_F(StatsTableTest, StatsCounter) {
+ // Create a stats table.
+ const std::wstring kTableName = L"StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ MockStatsCounter foo(L"foo");
+
+ // Test initial state.
+ EXPECT_TRUE(foo.Enabled());
+ EXPECT_NE(foo.Pointer(), static_cast<int*>(0));
+ EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+ EXPECT_EQ(0, *(foo.Pointer()));
+
+ // Test Increment.
+ while(*(foo.Pointer()) < 123) foo.Increment();
+ EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
+ foo.Add(0);
+ EXPECT_EQ(123, table.GetCounterValue(L"c:foo"));
+ foo.Add(-1);
+ EXPECT_EQ(122, table.GetCounterValue(L"c:foo"));
+
+ // Test Set.
+ foo.Set(0);
+ EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+ foo.Set(100);
+ EXPECT_EQ(100, table.GetCounterValue(L"c:foo"));
+ foo.Set(-1);
+ EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+ foo.Set(0);
+ EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+
+ // Test Decrement.
+ foo.Decrement(1);
+ EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+ foo.Decrement(0);
+ EXPECT_EQ(-1, table.GetCounterValue(L"c:foo"));
+ foo.Decrement(-1);
+ EXPECT_EQ(0, table.GetCounterValue(L"c:foo"));
+}
+
+class MockStatsCounterTimer : public StatsCounterTimer {
+ public:
+ MockStatsCounterTimer(const std::wstring& name)
+ : StatsCounterTimer(name) {}
+
+ TimeTicks start_time() { return start_time_; }
+ TimeTicks stop_time() { return stop_time_; }
+};
+
+// Test some basic StatsCounterTimer operations
+TEST_F(StatsTableTest, StatsCounterTimer) {
+ // Create a stats table.
+ const std::wstring kTableName = L"StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ MockStatsCounterTimer bar(L"bar");
+
+ // Test initial state.
+ EXPECT_FALSE(bar.Running());
+ EXPECT_TRUE(bar.start_time().is_null());
+ EXPECT_TRUE(bar.stop_time().is_null());
+
+ // Do some timing.
+ bar.Start();
+ Sleep(500);
+ bar.Stop();
+ EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
+
+ // Verify that timing again is additive.
+ bar.Start();
+ Sleep(500);
+ bar.Stop();
+ EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
+}
+
+// Test some basic StatsRate operations
+TEST_F(StatsTableTest, StatsRate) {
+ // Create a stats table.
+ const std::wstring kTableName = L"StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ StatsRate baz(L"baz");
+
+ // Test initial state.
+ EXPECT_FALSE(baz.Running());
+ EXPECT_EQ(0, table.GetCounterValue(L"c:baz"));
+ EXPECT_EQ(0, table.GetCounterValue(L"t:baz"));
+
+ // Do some timing.
+ baz.Start();
+ Sleep(500);
+ baz.Stop();
+ EXPECT_EQ(1, table.GetCounterValue(L"c:baz"));
+ EXPECT_LE(500, table.GetCounterValue(L"t:baz"));
+
+ // Verify that timing again is additive.
+ baz.Start();
+ Sleep(500);
+ baz.Stop();
+ EXPECT_EQ(2, table.GetCounterValue(L"c:baz"));
+ EXPECT_LE(1000, table.GetCounterValue(L"t:baz"));
+}
+
+// Test some basic StatsScope operations
+TEST_F(StatsTableTest, StatsScope) {
+ // Create a stats table.
+ const std::wstring kTableName = L"StatTable";
+ const int kMaxThreads = 20;
+ const int kMaxCounter = 5;
+ StatsTable table(kTableName, kMaxThreads, kMaxCounter);
+ StatsTable::set_current(&table);
+
+ StatsCounterTimer foo(L"foo");
+ StatsRate bar(L"bar");
+
+ // Test initial state.
+ EXPECT_EQ(0, table.GetCounterValue(L"t:foo"));
+ EXPECT_EQ(0, table.GetCounterValue(L"t:bar"));
+ EXPECT_EQ(0, table.GetCounterValue(L"c:bar"));
+
+ // Try a scope.
+ {
+ StatsScope<StatsCounterTimer> timer(foo);
+ StatsScope<StatsRate> timer2(bar);
+ Sleep(500);
+ }
+ EXPECT_LE(500, table.GetCounterValue(L"t:foo"));
+ EXPECT_LE(500, table.GetCounterValue(L"t:bar"));
+ EXPECT_EQ(1, table.GetCounterValue(L"c:bar"));
+
+ // Try a second scope.
+ {
+ StatsScope<StatsCounterTimer> timer(foo);
+ StatsScope<StatsRate> timer2(bar);
+ Sleep(500);
+ }
+ EXPECT_LE(1000, table.GetCounterValue(L"t:foo"));
+ EXPECT_LE(1000, table.GetCounterValue(L"t:bar"));
+ EXPECT_EQ(2, table.GetCounterValue(L"c:bar"));
+} \ No newline at end of file
diff --git a/base/string16.h b/base/string16.h
new file mode 100644
index 0000000..2466028
--- /dev/null
+++ b/base/string16.h
@@ -0,0 +1,217 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// WHAT:
+// A version of std::basic_string that works even on Linux when 2-byte wchar_t
+// values (-fshort-wchar) are used. You can access this class as std::string16.
+// We also define char16, which std::string16 is based upon.
+//
+// WHY:
+// Firefox uses 2-byte wide characters (UTF-16). On Windows, this is
+// mostly compatible with wchar_t, which is 2 bytes (UCS2).
+//
+// On Linux, sizeof(wchar_t) is 4 bytes by default. We can make it 2 bytes
+// using the GCC flag -fshort-wchar. But then std::wstring fails at run time,
+// because it calls some functions (like wcslen) that come from glibc -- which
+// was built with a 4-byte wchar_t!
+//
+// So we define std::string16, which is similar to std::wstring but replaces
+// all glibc functions with custom, 2-byte-char compatible routines. Fortuntely
+// for us, std::wstring uses mostly *inline* wchar_t-based functions (like
+// wmemcmp) that are defined in .h files and do not need to be overridden.
+
+#ifndef BASE_STRING16_H__
+#define BASE_STRING16_H__
+
+#include <string>
+
+#ifdef WIN32
+
+typedef wchar_t char16;
+
+namespace std {
+ typedef wstring string16;
+}
+
+#else
+
+typedef unsigned short char16;
+
+namespace std {
+ typedef basic_string<char16> string16;
+}
+
+
+// Define char16 versions of functions required below in char_traits<char16>
+extern "C" {
+
+ inline char16 *char16_wmemmove(char16 *s1, const char16 *s2, size_t n) {
+ return (char16 *)memmove(s1, s2, n * sizeof(char16));
+ }
+
+ inline char16 *char16_wmemcpy(char16 *s1, const char16 *s2, size_t n) {
+ return (char16 *)memcpy(s1, s2, n * sizeof(char16));
+ }
+
+ inline int char16_wmemcmp(const char16 *s1, const char16 *s2, size_t n) {
+ // we cannot call memcmp because that changes the semantics.
+ while (n > 0) {
+ if (*s1 != *s2) {
+ // we cannot use (*s1 - *s2) because char16 is unsigned
+ return ((*s1 < *s2) ? -1 : 1);
+ }
+ ++s1; ++s2; --n;
+ }
+ return 0;
+ }
+
+ inline const char16 *char16_wmemchr(const char16 *s, char16 c, size_t n) {
+ while (n > 0) {
+ if (*s == c) {
+ return s;
+ }
+ ++s; --n;
+ }
+ return 0;
+ }
+
+ inline char16 *char16_wmemset(char16 *s, char16 c, size_t n) {
+ char16 *s_orig = s;
+ while (n > 0) {
+ *s = c;
+ ++s; --n;
+ }
+ return s_orig;
+ }
+
+ inline size_t char16_wcslen(const char16 *s) {
+ const char16 *s_orig = s;
+ while (*s) { ++s; }
+ return (s - s_orig);
+ }
+
+} // END: extern "C"
+
+
+// Definition of char_traits<char16>, which enables basic_string<char16>
+//
+// This is a slightly modified version of char_traits<wchar_t> from gcc 3.2.2
+namespace std {
+
+ template<>
+ struct char_traits<char16>
+ {
+ typedef char16 char_type;
+ typedef wint_t int_type;
+ typedef streamoff off_type;
+ typedef wstreampos pos_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return char16_wmemcmp(__s1, __s2, __n); }
+
+ static size_t
+ length(const char_type* __s)
+ { return char16_wcslen(__s); }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return char16_wmemchr(__s, __a, __n); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, int_type __n)
+ { return char16_wmemmove(__s1, __s2, __n); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return char16_wmemcpy(__s1, __s2, __n); }
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ { return char16_wmemset(__s, __a, __n); }
+
+ static char_type
+ to_char_type(const int_type& __c) { return char_type(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c) { return int_type(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(WEOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return eq_int_type(__c, eof()) ? 0 : __c; }
+ };
+
+} // END: namespace std
+
+#endif // END: WIN32
+
+#endif // END: BASE_STRING16_H__
diff --git a/base/string_escape.cc b/base/string_escape.cc
new file mode 100644
index 0000000..018531e
--- /dev/null
+++ b/base/string_escape.cc
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_escape.h"
+
+#include <string>
+
+#include "base/string_util.h"
+
+namespace string_escape {
+
+// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful,
+// returns true and appends the escape sequence to |dst|.
+template<typename CHAR>
+static bool JavascriptSingleEscapeChar(const CHAR c, std::string* dst) {
+ switch (c) {
+ case '\b':
+ dst->append("\\b");
+ break;
+ case '\f':
+ dst->append("\\f");
+ break;
+ case '\n':
+ dst->append("\\n");
+ break;
+ case '\r':
+ dst->append("\\r");
+ break;
+ case '\t':
+ dst->append("\\t");
+ break;
+ case '\v':
+ dst->append("\\v");
+ break;
+ case '\\':
+ dst->append("\\\\");
+ break;
+ case '"':
+ dst->append("\\\"");
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+void JavascriptDoubleQuote(const std::wstring& str,
+ bool put_in_quotes,
+ std::string* dst) {
+ if (put_in_quotes)
+ dst->push_back('"');
+
+ for (std::wstring::const_iterator it = str.begin(); it != str.end(); ++it) {
+ wchar_t c = *it;
+ if (!JavascriptSingleEscapeChar(c, dst)) {
+ if (c > 255) {
+ // Non-ascii values need to be unicode dst->
+ // TODO(tc): Some unicode values are handled specially. See
+ // spidermonkey code.
+ StringAppendF(dst, "\\u%04X", c);
+ } else if (c < 32 || c > 126) {
+ // Spidermonkey hex escapes these values.
+ StringAppendF(dst, "\\x%02X", c);
+ } else {
+ dst->push_back(static_cast<char>(c));
+ }
+ }
+ }
+
+ if (put_in_quotes)
+ dst->push_back('"');
+}
+
+void JavascriptDoubleQuote(const std::string& str,
+ bool put_in_quotes,
+ std::string* dst) {
+ if (put_in_quotes)
+ dst->push_back('"');
+
+ for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) {
+ unsigned char c = *it;
+ if (!JavascriptSingleEscapeChar(c, dst)) {
+ // Hex encode if the character is non-printable 7bit ascii
+ if (c < 32 || c == 127) {
+ StringAppendF(dst, "\\x%02X", c);
+ } else {
+ dst->push_back(static_cast<char>(c));
+ }
+ }
+ }
+
+ if (put_in_quotes)
+ dst->push_back('"');
+}
+
+} // namespace string_escape
diff --git a/base/string_escape.h b/base/string_escape.h
new file mode 100644
index 0000000..a78ebe95
--- /dev/null
+++ b/base/string_escape.h
@@ -0,0 +1,60 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines utility functions for escaping strings.
+
+#ifndef BASE_STRING_ESCAPE_H__
+#define BASE_STRING_ESCAPE_H__
+
+#include <string>
+
+namespace string_escape {
+
+// Escape |str| appropriately for a javascript string litereal, _appending_ the
+// result to |dst|. This will create standard escape sequences (\b, \n),
+// hex escape sequences (\x00), and 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|.
+void JavascriptDoubleQuote(const std::wstring& str,
+ bool put_in_quotes,
+ std::string* dst);
+
+// Similar to the wide version, but for narrow strings. It will not use
+// \uXXXX unicode escape sequences. It will pass non-7bit characters directly
+// into the string unencoded, allowing the browser to interpret the encoding.
+// The outputted literal, when interpreted by the browser, could result in a
+// javascript string of a different length than the input |str|.
+void JavascriptDoubleQuote(const std::string& str,
+ bool put_in_quotes,
+ std::string* dst);
+
+} // namespace string_escape
+
+#endif // BASE_STRING_ESCAPE_H__
diff --git a/base/string_escape_unittest.cc b/base/string_escape_unittest.cc
new file mode 100644
index 0000000..91f15b7
--- /dev/null
+++ b/base/string_escape_unittest.cc
@@ -0,0 +1,79 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/string_escape.h"
+
+TEST(StringEscapeTest, JavascriptDoubleQuote) {
+ static const char* kToEscape = "\b\001aZ\"\\wee";
+ static const char* kEscaped = "\\b\\x01aZ\\\"\\\\wee";
+ static const char* kEscapedQuoted = "\"\\b\\x01aZ\\\"\\\\wee\"";
+ static const wchar_t* kUToEscape = L"\b\x0001" L"a\x123fZ\"\\wee";
+ static const char* kUEscaped = "\\b\\x01a\\u123FZ\\\"\\\\wee";
+ static const char* kUEscapedQuoted = "\"\\b\\x01a\\u123FZ\\\"\\\\wee\"";
+
+ std::string out;
+
+ // Test wide unicode escaping
+ out = "testy: ";
+ string_escape::JavascriptDoubleQuote(std::wstring(kUToEscape), false, &out);
+ ASSERT_EQ(std::string("testy: ") + kUEscaped, out);
+
+ out = "testy: ";
+ string_escape::JavascriptDoubleQuote(std::wstring(kUToEscape), true, &out);
+ ASSERT_EQ(std::string("testy: ") + kUEscapedQuoted, out);
+
+ // Test null and high bit / negative unicode values
+ std::wstring wstr(L"TeSt");
+ wstr.push_back(0);
+ wstr.push_back(0xffb1);
+ wstr.push_back(0x00ff);
+
+ out = "testy: ";
+ string_escape::JavascriptDoubleQuote(wstr, false, &out);
+ ASSERT_EQ("testy: TeSt\\x00\\uFFB1\\xFF", out);
+
+ // Test escaping of 7bit ascii
+ out = "testy: ";
+ string_escape::JavascriptDoubleQuote(std::string(kToEscape), false, &out);
+ ASSERT_EQ(std::string("testy: ") + kEscaped, out);
+
+ // Test null, non-printable, and non-7bit
+ std::string str("TeSt");
+ str.push_back(0);
+ str.push_back(15);
+ str.push_back(127);
+ str.push_back(-16);
+ str.push_back(-128);
+ str.push_back('!');
+
+ out = "testy: ";
+ string_escape::JavascriptDoubleQuote(str, false, &out);
+ ASSERT_EQ("testy: TeSt\\x00\\x0F\\x7F\xf0\x80!", out);
+}
diff --git a/base/string_piece.cc b/base/string_piece.cc
new file mode 100644
index 0000000..e2c0aa7
--- /dev/null
+++ b/base/string_piece.cc
@@ -0,0 +1,240 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from strings/stringpiece.cc with modifications
+
+#include <algorithm>
+#include <iostream>
+
+#include "base/string_piece.h"
+
+typedef StringPiece::size_type size_type;
+
+std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
+ o.write(piece.data(), static_cast<std::streamsize>(piece.size()));
+ return o;
+}
+
+bool operator==(const StringPiece& x, const StringPiece& y) {
+ if (x.size() != y.size())
+ return false;
+
+ return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+void StringPiece::CopyToString(std::string* target) const {
+ target->assign(!empty() ? data() : "", size());
+}
+
+void StringPiece::AppendToString(std::string* target) const {
+ if (!empty())
+ target->append(data(), size());
+}
+
+size_type StringPiece::copy(char* buf, size_type n, size_type pos) const {
+ size_type ret = std::min(length_ - pos, n);
+ memcpy(buf, ptr_ + pos, ret);
+ return ret;
+}
+
+size_type StringPiece::find(const StringPiece& s, size_type pos) const {
+ if (pos > length_)
+ return npos;
+
+ const char* result = std::search(ptr_ + pos, ptr_ + length_,
+ s.ptr_, s.ptr_ + s.length_);
+ const size_type xpos = result - ptr_;
+ return xpos + s.length_ <= length_ ? xpos : npos;
+}
+
+size_type StringPiece::find(char c, size_type pos) const {
+ if (pos >= length_)
+ return npos;
+
+ const char* result = std::find(ptr_ + pos, ptr_ + length_, c);
+ return result != ptr_ + length_ ? result - ptr_ : npos;
+}
+
+size_type StringPiece::rfind(const StringPiece& s, size_type pos) const {
+ if (length_ < s.length_)
+ return npos;
+
+ if (s.empty())
+ return std::min(length_, pos);
+
+ const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_;
+ const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_);
+ return result != last ? result - ptr_ : npos;
+}
+
+size_type StringPiece::rfind(char c, size_type pos) const {
+ if (length_ == 0)
+ return npos;
+
+ for (size_type i = std::min(pos, length_ - 1); ; --i) {
+ if (ptr_[i] == c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return npos;
+}
+
+// For each character in characters_wanted, sets the index corresponding
+// to the ASCII code of that character to 1 in table. This is used by
+// the find_.*_of methods below to tell whether or not a character is in
+// the lookup table in constant time.
+// The argument `table' must be an array that is large enough to hold all
+// the possible values of an unsigned char. Thus it should be be declared
+// as follows:
+// bool table[UCHAR_MAX + 1]
+static inline void BuildLookupTable(const StringPiece& characters_wanted,
+ bool* table) {
+ const size_type length = characters_wanted.length();
+ const char* const data = characters_wanted.data();
+ for (size_type i = 0; i < length; ++i) {
+ table[static_cast<unsigned char>(data[i])] = true;
+ }
+}
+
+size_type StringPiece::find_first_of(const StringPiece& s,
+ size_type pos) const {
+ if (length_ == 0 || s.length_ == 0)
+ return npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1)
+ return find_first_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_type i = pos; i < length_; ++i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+size_type StringPiece::find_first_not_of(const StringPiece& s,
+ size_type pos) const {
+ if (length_ == 0)
+ return npos;
+
+ if (s.length_ == 0)
+ return 0;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1)
+ return find_first_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_type i = pos; i < length_; ++i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])]) {
+ return i;
+ }
+ }
+ return npos;
+}
+
+size_type StringPiece::find_first_not_of(char c, size_type pos) const {
+ if (length_ == 0)
+ return npos;
+
+ for (; pos < length_; ++pos) {
+ if (ptr_[pos] != c) {
+ return pos;
+ }
+ }
+ return npos;
+}
+
+size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const {
+ if (length_ == 0 || s.length_ == 0)
+ return npos;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1)
+ return find_last_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (size_type i = std::min(pos, length_ - 1); ; --i) {
+ if (lookup[static_cast<unsigned char>(ptr_[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return npos;
+}
+
+size_type StringPiece::find_last_not_of(const StringPiece& s,
+ size_type pos) const {
+ if (length_ == 0)
+ return npos;
+
+ size_type i = std::min(pos, length_ - 1);
+ if (s.length_ == 0)
+ return i;
+
+ // Avoid the cost of BuildLookupTable() for a single-character search.
+ if (s.length_ == 1)
+ return find_last_not_of(s.ptr_[0], pos);
+
+ bool lookup[UCHAR_MAX + 1] = { false };
+ BuildLookupTable(s, lookup);
+ for (; ; --i) {
+ if (!lookup[static_cast<unsigned char>(ptr_[i])])
+ return i;
+ if (i == 0)
+ break;
+ }
+ return npos;
+}
+
+size_type StringPiece::find_last_not_of(char c, size_type pos) const {
+ if (length_ == 0)
+ return npos;
+
+ for (size_type i = std::min(pos, length_ - 1); ; --i) {
+ if (ptr_[i] != c)
+ return i;
+ if (i == 0)
+ break;
+ }
+ return npos;
+}
+
+StringPiece StringPiece::substr(size_type pos, size_type n) const {
+ if (pos > length_) pos = length_;
+ if (n > length_ - pos) n = length_ - pos;
+ return StringPiece(ptr_ + pos, n);
+}
+
+const StringPiece::size_type StringPiece::npos = size_type(-1);
diff --git a/base/string_piece.h b/base/string_piece.h
new file mode 100644
index 0000000..8cb375e
--- /dev/null
+++ b/base/string_piece.h
@@ -0,0 +1,209 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// Copied from strings/stringpiece.h with modifications
+//
+// A string-like object that points to a sized piece of memory.
+//
+// Functions or methods may use const StringPiece& parameters to accept either
+// a "const char*" or a "string" value that will be implicitly converted to
+// a StringPiece. The implicit conversion means that it is often appropriate
+// to include this .h file in other files rather than forward-declaring
+// StringPiece as would be appropriate for most other Google classes.
+//
+// Systematic usage of StringPiece is encouraged as it will reduce unnecessary
+// conversions from "const char*" to "string" and back again.
+//
+
+#ifndef BASE_STRING_PIECE_H__
+#define BASE_STRING_PIECE_H__
+
+#include <algorithm>
+#include <iosfwd>
+#include <string>
+
+#include "base/basictypes.h"
+
+class StringPiece {
+ public:
+ typedef size_t size_type;
+
+ private:
+ const char* ptr_;
+ size_type length_;
+
+ public:
+ // We provide non-explicit singleton constructors so users can pass
+ // in a "const char*" or a "string" wherever a "StringPiece" is
+ // expected.
+ StringPiece() : ptr_(NULL), length_(0) { }
+ StringPiece(const char* str)
+ : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { }
+ StringPiece(const std::string& str)
+ : ptr_(str.data()), length_(str.size()) { }
+ StringPiece(const char* offset, size_type len)
+ : ptr_(offset), length_(len) { }
+
+ // data() may return a pointer to a buffer with embedded NULs, and the
+ // returned buffer may or may not be null terminated. Therefore it is
+ // typically a mistake to pass data() to a routine that expects a NUL
+ // terminated string.
+ const char* data() const { return ptr_; }
+ size_type size() const { return length_; }
+ size_type length() const { return length_; }
+ bool empty() const { return length_ == 0; }
+
+ void clear() { ptr_ = NULL; length_ = 0; }
+ void set(const char* data, size_type len) { ptr_ = data; length_ = len; }
+ void set(const char* str) {
+ ptr_ = str;
+ length_ = str ? strlen(str) : 0;
+ }
+ void set(const void* data, size_type len) {
+ ptr_ = reinterpret_cast<const char*>(data);
+ length_ = len;
+ }
+
+ char operator[](size_type i) const { return ptr_[i]; }
+
+ void remove_prefix(size_type n) {
+ ptr_ += n;
+ length_ -= n;
+ }
+
+ void remove_suffix(size_type n) {
+ length_ -= n;
+ }
+
+ int compare(const StringPiece& x) const {
+ int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_));
+ if (r == 0) {
+ if (length_ < x.length_) r = -1;
+ else if (length_ > x.length_) r = +1;
+ }
+ return r;
+ }
+
+ std::string as_string() const {
+ // std::string doesn't like to take a NULL pointer even with a 0 size.
+ return std::string(!empty() ? data() : "", size());
+ }
+
+ void CopyToString(std::string* target) const;
+ void AppendToString(std::string* target) const;
+
+ // Does "this" start with "x"
+ bool starts_with(const StringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (wordmemcmp(ptr_, x.ptr_, x.length_) == 0));
+ }
+
+ // Does "this" end with "x"
+ bool ends_with(const StringPiece& x) const {
+ return ((length_ >= x.length_) &&
+ (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0));
+ }
+
+ // standard STL container boilerplate
+ typedef char value_type;
+ typedef const char* pointer;
+ typedef const char& reference;
+ typedef const char& const_reference;
+ typedef ptrdiff_t difference_type;
+ static const size_type npos;
+ typedef const char* const_iterator;
+ typedef const char* iterator;
+ typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+ iterator begin() const { return ptr_; }
+ iterator end() const { return ptr_ + length_; }
+ const_reverse_iterator rbegin() const {
+ return const_reverse_iterator(ptr_ + length_);
+ }
+ const_reverse_iterator rend() const {
+ return const_reverse_iterator(ptr_);
+ }
+
+ size_type max_size() const { return length_; }
+ size_type capacity() const { return length_; }
+
+ size_type copy(char* buf, size_type n, size_type pos = 0) const;
+
+ size_type find(const StringPiece& s, size_type pos = 0) const;
+ size_type find(char c, size_type pos = 0) const;
+ size_type rfind(const StringPiece& s, size_type pos = npos) const;
+ size_type rfind(char c, size_type pos = npos) const;
+
+ size_type find_first_of(const StringPiece& s, size_type pos = 0) const;
+ size_type find_first_of(char c, size_type pos = 0) const {
+ return find(c, pos);
+ }
+ size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const;
+ size_type find_first_not_of(char c, size_type pos = 0) const;
+ size_type find_last_of(const StringPiece& s, size_type pos = npos) const;
+ size_type find_last_of(char c, size_type pos = npos) const {
+ return rfind(c, pos);
+ }
+ size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const;
+ size_type find_last_not_of(char c, size_type pos = npos) const;
+
+ StringPiece substr(size_type pos, size_type n = npos) const;
+
+ static int wordmemcmp(const char* p, const char* p2, size_type N) {
+ return memcmp(p, p2, N);
+ }
+};
+
+bool operator==(const StringPiece& x, const StringPiece& y);
+
+inline bool operator!=(const StringPiece& x, const StringPiece& y) {
+ return !(x == y);
+}
+
+inline bool operator<(const StringPiece& x, const StringPiece& y) {
+ const int r = StringPiece::wordmemcmp(x.data(), y.data(),
+ std::min(x.size(), y.size()));
+ return ((r < 0) || ((r == 0) && (x.size() < y.size())));
+}
+
+inline bool operator>(const StringPiece& x, const StringPiece& y) {
+ return y < x;
+}
+
+inline bool operator<=(const StringPiece& x, const StringPiece& y) {
+ return !(x > y);
+}
+
+inline bool operator>=(const StringPiece& x, const StringPiece& y) {
+ return !(x < y);
+}
+
+// allow StringPiece to be logged (needed for unit testing).
+extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece);
+
+#endif // BASE_STRING_PIECE_H__
diff --git a/base/string_piece_unittest.cc b/base/string_piece_unittest.cc
new file mode 100644
index 0000000..af8f6f4
--- /dev/null
+++ b/base/string_piece_unittest.cc
@@ -0,0 +1,565 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include "base/string_piece.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(StringPieceTest, CheckComparisonOperators) {
+#define CMP_Y(op, x, y) \
+ ASSERT_TRUE( (StringPiece((x)) op StringPiece((y)))); \
+ ASSERT_TRUE( (StringPiece((x)).compare(StringPiece((y))) op 0))
+
+#define CMP_N(op, x, y) \
+ ASSERT_FALSE(StringPiece((x)) op StringPiece((y))); \
+ ASSERT_FALSE(StringPiece((x)).compare(StringPiece((y))) op 0)
+
+ CMP_Y(==, "", "");
+ CMP_Y(==, "a", "a");
+ CMP_Y(==, "aa", "aa");
+ CMP_N(==, "a", "");
+ CMP_N(==, "", "a");
+ CMP_N(==, "a", "b");
+ CMP_N(==, "a", "aa");
+ CMP_N(==, "aa", "a");
+
+ CMP_N(!=, "", "");
+ CMP_N(!=, "a", "a");
+ CMP_N(!=, "aa", "aa");
+ CMP_Y(!=, "a", "");
+ CMP_Y(!=, "", "a");
+ CMP_Y(!=, "a", "b");
+ CMP_Y(!=, "a", "aa");
+ CMP_Y(!=, "aa", "a");
+
+ CMP_Y(<, "a", "b");
+ CMP_Y(<, "a", "aa");
+ CMP_Y(<, "aa", "b");
+ CMP_Y(<, "aa", "bb");
+ CMP_N(<, "a", "a");
+ CMP_N(<, "b", "a");
+ CMP_N(<, "aa", "a");
+ CMP_N(<, "b", "aa");
+ CMP_N(<, "bb", "aa");
+
+ CMP_Y(<=, "a", "a");
+ CMP_Y(<=, "a", "b");
+ CMP_Y(<=, "a", "aa");
+ CMP_Y(<=, "aa", "b");
+ CMP_Y(<=, "aa", "bb");
+ CMP_N(<=, "b", "a");
+ CMP_N(<=, "aa", "a");
+ CMP_N(<=, "b", "aa");
+ CMP_N(<=, "bb", "aa");
+
+ CMP_N(>=, "a", "b");
+ CMP_N(>=, "a", "aa");
+ CMP_N(>=, "aa", "b");
+ CMP_N(>=, "aa", "bb");
+ CMP_Y(>=, "a", "a");
+ CMP_Y(>=, "b", "a");
+ CMP_Y(>=, "aa", "a");
+ CMP_Y(>=, "b", "aa");
+ CMP_Y(>=, "bb", "aa");
+
+ CMP_N(>, "a", "a");
+ CMP_N(>, "a", "b");
+ CMP_N(>, "a", "aa");
+ CMP_N(>, "aa", "b");
+ CMP_N(>, "aa", "bb");
+ CMP_Y(>, "b", "a");
+ CMP_Y(>, "aa", "a");
+ CMP_Y(>, "b", "aa");
+ CMP_Y(>, "bb", "aa");
+
+ std::string x;
+ for (int i = 0; i < 256; i++) {
+ x += 'a';
+ std::string y = x;
+ CMP_Y(==, x, y);
+ for (int j = 0; j < i; j++) {
+ std::string z = x;
+ z[j] = 'b'; // Differs in position 'j'
+ CMP_N(==, x, z);
+ }
+ }
+
+#undef CMP_Y
+#undef CMP_N
+}
+
+TEST(StringPieceTest, CheckSTL) {
+ StringPiece a("abcdefghijklmnopqrstuvwxyz");
+ StringPiece b("abc");
+ StringPiece c("xyz");
+ StringPiece d("foobar");
+ StringPiece e;
+ std::string temp("123");
+ temp += '\0';
+ temp += "456";
+ StringPiece f(temp);
+
+ ASSERT_EQ(a[6], 'g');
+ ASSERT_EQ(b[0], 'a');
+ ASSERT_EQ(c[2], 'z');
+ ASSERT_EQ(f[3], '\0');
+ ASSERT_EQ(f[5], '5');
+
+ ASSERT_EQ(*d.data(), 'f');
+ ASSERT_EQ(d.data()[5], 'r');
+ ASSERT_TRUE(e.data() == NULL);
+
+ ASSERT_EQ(*a.begin(), 'a');
+ ASSERT_EQ(*(b.begin() + 2), 'c');
+ ASSERT_EQ(*(c.end() - 1), 'z');
+
+ ASSERT_EQ(*a.rbegin(), 'z');
+ ASSERT_EQ(*(b.rbegin() + 2), 'a');
+ ASSERT_EQ(*(c.rend() - 1), 'x');
+ ASSERT_TRUE(a.rbegin() + 26 == a.rend());
+
+ ASSERT_EQ(a.size(), 26);
+ ASSERT_EQ(b.size(), 3);
+ ASSERT_EQ(c.size(), 3);
+ ASSERT_EQ(d.size(), 6);
+ ASSERT_EQ(e.size(), 0);
+ ASSERT_EQ(f.size(), 7);
+
+ ASSERT_TRUE(!d.empty());
+ ASSERT_TRUE(d.begin() != d.end());
+ ASSERT_TRUE(d.begin() + 6 == d.end());
+
+ ASSERT_TRUE(e.empty());
+ ASSERT_TRUE(e.begin() == e.end());
+
+ d.clear();
+ ASSERT_EQ(d.size(), 0);
+ ASSERT_TRUE(d.empty());
+ ASSERT_TRUE(d.data() == NULL);
+ ASSERT_TRUE(d.begin() == d.end());
+
+ ASSERT_GE(a.max_size(), a.capacity());
+ ASSERT_GE(a.capacity(), a.size());
+
+ char buf[4] = { '%', '%', '%', '%' };
+ ASSERT_EQ(a.copy(buf, 4), 4);
+ ASSERT_EQ(buf[0], a[0]);
+ ASSERT_EQ(buf[1], a[1]);
+ ASSERT_EQ(buf[2], a[2]);
+ ASSERT_EQ(buf[3], a[3]);
+ ASSERT_EQ(a.copy(buf, 3, 7), 3);
+ ASSERT_EQ(buf[0], a[7]);
+ ASSERT_EQ(buf[1], a[8]);
+ ASSERT_EQ(buf[2], a[9]);
+ ASSERT_EQ(buf[3], a[3]);
+ ASSERT_EQ(c.copy(buf, 99), 3);
+ ASSERT_EQ(buf[0], c[0]);
+ ASSERT_EQ(buf[1], c[1]);
+ ASSERT_EQ(buf[2], c[2]);
+ ASSERT_EQ(buf[3], a[3]);
+
+ ASSERT_EQ(StringPiece::npos, std::string::npos);
+
+ ASSERT_EQ(a.find(b), 0);
+ ASSERT_EQ(a.find(b, 1), StringPiece::npos);
+ ASSERT_EQ(a.find(c), 23);
+ ASSERT_EQ(a.find(c, 9), 23);
+ ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos);
+ ASSERT_EQ(b.find(c), StringPiece::npos);
+ ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos);
+ ASSERT_EQ(a.find(d), 0);
+ ASSERT_EQ(a.find(e), 0);
+ ASSERT_EQ(a.find(d, 12), 12);
+ ASSERT_EQ(a.find(e, 17), 17);
+ StringPiece g("xx not found bb");
+ ASSERT_EQ(a.find(g), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.find(b), StringPiece::npos);
+ ASSERT_EQ(e.find(b), StringPiece::npos);
+ ASSERT_EQ(d.find(b, 4), StringPiece::npos);
+ ASSERT_EQ(e.find(b, 7), StringPiece::npos);
+
+ size_t empty_search_pos = std::string().find(std::string());
+ ASSERT_EQ(d.find(d), empty_search_pos);
+ ASSERT_EQ(d.find(e), empty_search_pos);
+ ASSERT_EQ(e.find(d), empty_search_pos);
+ ASSERT_EQ(e.find(e), empty_search_pos);
+ ASSERT_EQ(d.find(d, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(d.find(e, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(e.find(d, 4), std::string().find(std::string(), 4));
+ ASSERT_EQ(e.find(e, 4), std::string().find(std::string(), 4));
+
+ ASSERT_EQ(a.find('a'), 0);
+ ASSERT_EQ(a.find('c'), 2);
+ ASSERT_EQ(a.find('z'), 25);
+ ASSERT_EQ(a.find('$'), StringPiece::npos);
+ ASSERT_EQ(a.find('\0'), StringPiece::npos);
+ ASSERT_EQ(f.find('\0'), 3);
+ ASSERT_EQ(f.find('3'), 2);
+ ASSERT_EQ(f.find('5'), 5);
+ ASSERT_EQ(g.find('o'), 4);
+ ASSERT_EQ(g.find('o', 4), 4);
+ ASSERT_EQ(g.find('o', 5), 8);
+ ASSERT_EQ(a.find('b', 5), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.find('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find('\0'), StringPiece::npos);
+ ASSERT_EQ(d.find('\0', 4), StringPiece::npos);
+ ASSERT_EQ(e.find('\0', 7), StringPiece::npos);
+ ASSERT_EQ(d.find('x'), StringPiece::npos);
+ ASSERT_EQ(e.find('x'), StringPiece::npos);
+ ASSERT_EQ(d.find('x', 4), StringPiece::npos);
+ ASSERT_EQ(e.find('x', 7), StringPiece::npos);
+
+ ASSERT_EQ(a.rfind(b), 0);
+ ASSERT_EQ(a.rfind(b, 1), 0);
+ ASSERT_EQ(a.rfind(c), 23);
+ ASSERT_EQ(a.rfind(c, 22), StringPiece::npos);
+ ASSERT_EQ(a.rfind(c, 1), StringPiece::npos);
+ ASSERT_EQ(a.rfind(c, 0), StringPiece::npos);
+ ASSERT_EQ(b.rfind(c), StringPiece::npos);
+ ASSERT_EQ(b.rfind(c, 0), StringPiece::npos);
+ ASSERT_EQ(a.rfind(d), a.as_string().rfind(std::string()));
+ ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string()));
+ ASSERT_EQ(a.rfind(d, 12), 12);
+ ASSERT_EQ(a.rfind(e, 17), 17);
+ ASSERT_EQ(a.rfind(g), StringPiece::npos);
+ ASSERT_EQ(d.rfind(b), StringPiece::npos);
+ ASSERT_EQ(e.rfind(b), StringPiece::npos);
+ ASSERT_EQ(d.rfind(b, 4), StringPiece::npos);
+ ASSERT_EQ(e.rfind(b, 7), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(e, 4), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(e, 7), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(d), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(d), std::string().rfind(std::string()));
+ ASSERT_EQ(d.rfind(e), std::string().rfind(std::string()));
+ ASSERT_EQ(e.rfind(e), std::string().rfind(std::string()));
+
+ ASSERT_EQ(g.rfind('o'), 8);
+ ASSERT_EQ(g.rfind('q'), StringPiece::npos);
+ ASSERT_EQ(g.rfind('o', 8), 8);
+ ASSERT_EQ(g.rfind('o', 7), 4);
+ ASSERT_EQ(g.rfind('o', 3), StringPiece::npos);
+ ASSERT_EQ(f.rfind('\0'), 3);
+ ASSERT_EQ(f.rfind('\0', 12), 3);
+ ASSERT_EQ(f.rfind('3'), 2);
+ ASSERT_EQ(f.rfind('5'), 5);
+ // empty string nonsense
+ ASSERT_EQ(d.rfind('o'), StringPiece::npos);
+ ASSERT_EQ(e.rfind('o'), StringPiece::npos);
+ ASSERT_EQ(d.rfind('o', 4), StringPiece::npos);
+ ASSERT_EQ(e.rfind('o', 7), StringPiece::npos);
+
+ ASSERT_EQ(a.find_first_of(b), 0);
+ ASSERT_EQ(a.find_first_of(b, 0), 0);
+ ASSERT_EQ(a.find_first_of(b, 1), 1);
+ ASSERT_EQ(a.find_first_of(b, 2), 2);
+ ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(c), 23);
+ ASSERT_EQ(a.find_first_of(c, 23), 23);
+ ASSERT_EQ(a.find_first_of(c, 24), 24);
+ ASSERT_EQ(a.find_first_of(c, 25), 25);
+ ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos);
+ ASSERT_EQ(g.find_first_of(b), 13);
+ ASSERT_EQ(g.find_first_of(c), 0);
+ ASSERT_EQ(a.find_first_of(f), StringPiece::npos);
+ ASSERT_EQ(f.find_first_of(a), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(a.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(a.find_first_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(b), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(b), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_first_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_first_of(e), StringPiece::npos);
+
+ ASSERT_EQ(a.find_first_not_of(b), 3);
+ ASSERT_EQ(a.find_first_not_of(c), 0);
+ ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(f.find_first_not_of(a), 0);
+ ASSERT_EQ(a.find_first_not_of(f), 0);
+ ASSERT_EQ(a.find_first_not_of(d), 0);
+ ASSERT_EQ(a.find_first_not_of(e), 0);
+ // empty string nonsense
+ ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos);
+
+ StringPiece h("====");
+ ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos);
+ ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos);
+ ASSERT_EQ(h.find_first_not_of('\0'), 0);
+ ASSERT_EQ(g.find_first_not_of('x'), 2);
+ ASSERT_EQ(f.find_first_not_of('\0'), 0);
+ ASSERT_EQ(f.find_first_not_of('\0', 3), 4);
+ ASSERT_EQ(f.find_first_not_of('\0', 2), 2);
+ // empty string nonsense
+ ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos);
+
+ // StringPiece g("xx not found bb");
+ StringPiece i("56");
+ ASSERT_EQ(h.find_last_of(a), StringPiece::npos);
+ ASSERT_EQ(g.find_last_of(a), g.size()-1);
+ ASSERT_EQ(a.find_last_of(b), 2);
+ ASSERT_EQ(a.find_last_of(c), a.size()-1);
+ ASSERT_EQ(f.find_last_of(i), 6);
+ ASSERT_EQ(a.find_last_of('a'), 0);
+ ASSERT_EQ(a.find_last_of('b'), 1);
+ ASSERT_EQ(a.find_last_of('z'), 25);
+ ASSERT_EQ(a.find_last_of('a', 5), 0);
+ ASSERT_EQ(a.find_last_of('b', 5), 1);
+ ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos);
+ ASSERT_EQ(a.find_last_of('z', 25), 25);
+ ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(i, 5), 5);
+ ASSERT_EQ(f.find_last_of(i, 6), 6);
+ ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(f.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(f), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(f), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos);
+
+ ASSERT_EQ(a.find_last_not_of(b), a.size()-1);
+ ASSERT_EQ(a.find_last_not_of(c), 22);
+ ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos);
+ ASSERT_EQ(f.find_last_not_of(i), 4);
+ ASSERT_EQ(a.find_last_not_of(c, 24), 22);
+ ASSERT_EQ(a.find_last_not_of(b, 3), 3);
+ ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos);
+ // empty string nonsense
+ ASSERT_EQ(f.find_last_not_of(d), f.size()-1);
+ ASSERT_EQ(f.find_last_not_of(e), f.size()-1);
+ ASSERT_EQ(f.find_last_not_of(d, 4), 4);
+ ASSERT_EQ(f.find_last_not_of(e, 4), 4);
+ ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos);
+
+ ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1);
+ ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of('c'), 1);
+ ASSERT_EQ(h.find_last_not_of('x', 2), 2);
+ ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos);
+ ASSERT_EQ(b.find_last_not_of('b', 1), 0);
+ // empty string nonsense
+ ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos);
+ ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos);
+ ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos);
+
+ ASSERT_EQ(a.substr(0, 3), b);
+ ASSERT_EQ(a.substr(23), c);
+ ASSERT_EQ(a.substr(23, 3), c);
+ ASSERT_EQ(a.substr(23, 99), c);
+ ASSERT_EQ(a.substr(0), a);
+ ASSERT_EQ(a.substr(3, 2), "de");
+ // empty string nonsense
+ ASSERT_EQ(a.substr(99, 2), e);
+ ASSERT_EQ(d.substr(99), e);
+ ASSERT_EQ(d.substr(0, 99), e);
+ ASSERT_EQ(d.substr(99, 99), e);
+}
+
+TEST(StringPieceTest, CheckCustom) {
+ StringPiece a("foobar");
+ std::string s1("123");
+ s1 += '\0';
+ s1 += "456";
+ StringPiece b(s1);
+ StringPiece e;
+ std::string s2;
+
+ // CopyToString
+ a.CopyToString(&s2);
+ ASSERT_EQ(s2.size(), 6);
+ ASSERT_EQ(s2, "foobar");
+ b.CopyToString(&s2);
+ ASSERT_EQ(s2.size(), 7);
+ ASSERT_EQ(s1, s2);
+ e.CopyToString(&s2);
+ ASSERT_TRUE(s2.empty());
+
+ // AppendToString
+ s2.erase();
+ a.AppendToString(&s2);
+ ASSERT_EQ(s2.size(), 6);
+ ASSERT_EQ(s2, "foobar");
+ a.AppendToString(&s2);
+ ASSERT_EQ(s2.size(), 12);
+ ASSERT_EQ(s2, "foobarfoobar");
+
+ // starts_with
+ ASSERT_TRUE(a.starts_with(a));
+ ASSERT_TRUE(a.starts_with("foo"));
+ ASSERT_TRUE(a.starts_with(e));
+ ASSERT_TRUE(b.starts_with(s1));
+ ASSERT_TRUE(b.starts_with(b));
+ ASSERT_TRUE(b.starts_with(e));
+ ASSERT_TRUE(e.starts_with(""));
+ ASSERT_TRUE(!a.starts_with(b));
+ ASSERT_TRUE(!b.starts_with(a));
+ ASSERT_TRUE(!e.starts_with(a));
+
+ // ends with
+ ASSERT_TRUE(a.ends_with(a));
+ ASSERT_TRUE(a.ends_with("bar"));
+ ASSERT_TRUE(a.ends_with(e));
+ ASSERT_TRUE(b.ends_with(s1));
+ ASSERT_TRUE(b.ends_with(b));
+ ASSERT_TRUE(b.ends_with(e));
+ ASSERT_TRUE(e.ends_with(""));
+ ASSERT_TRUE(!a.ends_with(b));
+ ASSERT_TRUE(!b.ends_with(a));
+ ASSERT_TRUE(!e.ends_with(a));
+
+ // remove_prefix
+ StringPiece c(a);
+ c.remove_prefix(3);
+ ASSERT_EQ(c, "bar");
+ c = a;
+ c.remove_prefix(0);
+ ASSERT_EQ(c, a);
+ c.remove_prefix(c.size());
+ ASSERT_EQ(c, e);
+
+ // remove_suffix
+ c = a;
+ c.remove_suffix(3);
+ ASSERT_EQ(c, "foo");
+ c = a;
+ c.remove_suffix(0);
+ ASSERT_EQ(c, a);
+ c.remove_suffix(c.size());
+ ASSERT_EQ(c, e);
+
+ // set
+ c.set("foobar", 6);
+ ASSERT_EQ(c, a);
+ c.set("foobar", 0);
+ ASSERT_EQ(c, e);
+ c.set("foobar", 7);
+ ASSERT_NE(c, a);
+
+ c.set("foobar");
+ ASSERT_EQ(c, a);
+
+ c.set(static_cast<const void*>("foobar"), 6);
+ ASSERT_EQ(c, a);
+ c.set(static_cast<const void*>("foobar"), 0);
+ ASSERT_EQ(c, e);
+ c.set(static_cast<const void*>("foobar"), 7);
+ ASSERT_NE(c, a);
+
+ // as_string
+ std::string s3(a.as_string().c_str(), 7);
+ ASSERT_EQ(c, s3);
+ std::string s4(e.as_string());
+ ASSERT_TRUE(s4.empty());
+}
+
+TEST(StringPieceTest, CheckNULL) {
+ // we used to crash here, but now we don't.
+ StringPiece s(NULL);
+ ASSERT_EQ(s.data(), (const char*)NULL);
+ ASSERT_EQ(s.size(), 0);
+
+ s.set(NULL);
+ ASSERT_EQ(s.data(), (const char*)NULL);
+ ASSERT_EQ(s.size(), 0);
+}
+
+TEST(StringPieceTest, CheckComparisons2) {
+ StringPiece abc("abcdefghijklmnopqrstuvwxyz");
+
+ // check comparison operations on strings longer than 4 bytes.
+ ASSERT_TRUE(abc == StringPiece("abcdefghijklmnopqrstuvwxyz"));
+ ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyz")) == 0);
+
+ ASSERT_TRUE(abc < StringPiece("abcdefghijklmnopqrstuvwxzz"));
+ ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxzz")) < 0);
+
+ ASSERT_TRUE(abc > StringPiece("abcdefghijklmnopqrstuvwxyy"));
+ ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyy")) > 0);
+
+ // starts_with
+ ASSERT_TRUE(abc.starts_with(abc));
+ ASSERT_TRUE(abc.starts_with("abcdefghijklm"));
+ ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz"));
+
+ // ends_with
+ ASSERT_TRUE(abc.ends_with(abc));
+ ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz"));
+ ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz"));
+}
+
+TEST(StringPieceTest, StringCompareNotAmbiguous) {
+ ASSERT_TRUE("hello" == std::string("hello"));
+ ASSERT_TRUE("hello" < std::string("world"));
+}
+
+TEST(StringPieceTest, HeterogenousStringPieceEquals) {
+ ASSERT_TRUE(StringPiece("hello") == std::string("hello"));
+ ASSERT_TRUE("hello" == StringPiece("hello"));
+}
diff --git a/base/string_tokenizer.h b/base/string_tokenizer.h
new file mode 100644
index 0000000..9b0c468
--- /dev/null
+++ b/base/string_tokenizer.h
@@ -0,0 +1,225 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STRING_TOKENIZER_H__
+#define BASE_STRING_TOKENIZER_H__
+
+#include <string>
+
+// StringTokenizerT is a simple string tokenizer class. It works like an
+// iterator that with each step (see the Advance method) updates members that
+// refer to the next token in the input string. The user may optionally
+// configure the tokenizer to return delimiters.
+//
+//
+// EXAMPLE 1:
+//
+// StringTokenizer t("this is a test", " ");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// this
+// is
+// a
+// test
+//
+//
+// EXAMPLE 2:
+//
+// StringTokenizer t("no-cache=\"foo, bar\", private", ", ");
+// t.set_quote_chars("\"");
+// while (t.GetNext()) {
+// printf("%s\n", t.token().c_str());
+// }
+//
+// Output:
+//
+// no-cache="foo, bar"
+// private
+//
+//
+// EXAMPLE 3:
+//
+// bool next_is_option = false, next_is_value = false;
+// std::string input = "text/html; charset=UTF-8; foo=bar";
+// StringTokenizer t(input, "; =");
+// t.set_options(StringTokenizer::RETURN_DELIMS);
+// while (t.GetNext()) {
+// if (t.token_is_delim()) {
+// switch (*t.token_begin()) {
+// case ';':
+// next_is_option = true;
+// break;
+// case '=':
+// next_is_value = true;
+// break;
+// }
+// } else {
+// const char* label;
+// if (next_is_option) {
+// label = "option-name";
+// next_is_option = false;
+// } else if (next_is_value) {
+// label = "option-value";
+// next_is_value = false;
+// } else {
+// label = "mime-type";
+// }
+// printf("%s: %s\n", label, t.token().c_str());
+// }
+// }
+//
+//
+template <class str>
+class StringTokenizerT {
+ public:
+ typedef typename str::const_iterator const_iterator;
+ typedef typename str::value_type char_type;
+
+ // Options that may be pass to set_options()
+ enum {
+ // Specifies the delimiters should be returned as tokens
+ RETURN_DELIMS = 1 << 0,
+ };
+
+ StringTokenizerT(const str& string,
+ const str& delims) {
+ Init(string.begin(), string.end(), delims);
+ }
+
+ StringTokenizerT(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ Init(string_begin, string_end, delims);
+ }
+
+ // Set the options for this tokenizer. By default, this is 0.
+ void set_options(int options) { options_ = options; }
+
+ // Set the characters to regard as quotes. By default, this is empty. When
+ // a quote char is encountered, the tokenizer will switch into a mode where
+ // it ignores delimiters that it finds. It switches out of this mode once it
+ // finds another instance of the quote char. If a backslash is encountered
+ // within a quoted string, then the next character is skipped.
+ void set_quote_chars(const std::string& quotes) { quotes_ = quotes; }
+
+ // Call this method to advance the tokenizer to the next delimiter. This
+ // returns false if the tokenizer is complete. This method must be called
+ // before calling any of the token* methods.
+ bool GetNext() {
+ AdvanceState state;
+ token_is_delim_ = false;
+ for (;;) {
+ token_begin_ = token_end_;
+ if (token_end_ == end_)
+ return false;
+ ++token_end_;
+ if (AdvanceOne(&state, *token_begin_))
+ break;
+ if (options_ & RETURN_DELIMS) {
+ token_is_delim_ = true;
+ return true;
+ }
+ // else skip over delim
+ }
+ while (token_end_ != end_ && AdvanceOne(&state, *token_end_))
+ ++token_end_;
+ return true;
+ }
+
+ // Returns true if token is a delimiter. When the tokenizer is constructed
+ // with the RETURN_DELIMS option, this method can be used to check if the
+ // returned token is actually a delimiter.
+ bool token_is_delim() const { return token_is_delim_; }
+
+ // If GetNext() returned true, then these methods may be used to read the
+ // value of the token.
+ const_iterator token_begin() const { return token_begin_; }
+ const_iterator token_end() const { return token_end_; }
+ str token() const { return str(token_begin_, token_end_); }
+
+ private:
+ void Init(const_iterator string_begin,
+ const_iterator string_end,
+ const str& delims) {
+ token_end_ = string_begin;
+ end_ = string_end;
+ delims_ = delims;
+ options_ = 0;
+ }
+
+ bool IsDelim(char_type c) const {
+ return delims_.find(c) != str::npos;
+ }
+
+ bool IsQuote(char_type c) const {
+ return quotes_.find(c) != str::npos;
+ }
+
+ struct AdvanceState {
+ bool in_quote;
+ bool in_escape;
+ char_type quote_char;
+ AdvanceState() : in_quote(false), in_escape(false) {}
+ };
+
+ // Returns true if a delimiter was not hit.
+ bool AdvanceOne(AdvanceState* state, char_type c) {
+ if (state->in_quote) {
+ if (state->in_escape) {
+ state->in_escape = false;
+ } else if (c == '\\') {
+ state->in_escape = true;
+ } else if (c == state->quote_char) {
+ state->in_quote = false;
+ }
+ } else {
+ if (IsDelim(c))
+ return false;
+ state->in_quote = IsQuote(state->quote_char = c);
+ }
+ return true;
+ }
+
+ const_iterator token_begin_;
+ const_iterator token_end_;
+ const_iterator end_;
+ str delims_;
+ str quotes_;
+ int options_;
+ bool token_is_delim_;
+};
+
+typedef StringTokenizerT<std::string> StringTokenizer;
+typedef StringTokenizerT<std::wstring> WStringTokenizer;
+
+#endif // BASE_STRING_TOKENIZER_H__
diff --git a/base/string_tokenizer_unittest.cc b/base/string_tokenizer_unittest.cc
new file mode 100644
index 0000000..16b8018
--- /dev/null
+++ b/base/string_tokenizer_unittest.cc
@@ -0,0 +1,232 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_tokenizer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace {
+class StringTokenizerTest : public testing::Test {};
+}
+
+TEST(StringTokenizerTest, Simple) {
+ string input = "this is a test";
+ StringTokenizer t(input, " ");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, RetDelims) {
+ string input = "this is a test";
+ StringTokenizer t(input, " ");
+ t.set_options(StringTokenizer::RETURN_DELIMS);
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ManyDelims) {
+ string input = "this: is, a-test";
+ StringTokenizer t(input, ": ,-");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("this"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("is"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("a"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("test"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseHeader) {
+ string input = "Content-Type: text/html ; charset=UTF-8";
+ StringTokenizer t(input, ": ;=");
+ t.set_options(StringTokenizer::RETURN_DELIMS);
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("Content-Type"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(":"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("text/html"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(";"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string(" "), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("charset"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_TRUE(t.token_is_delim());
+ EXPECT_EQ(string("="), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+ EXPECT_EQ(string("UTF-8"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+ EXPECT_FALSE(t.token_is_delim());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString) {
+ string input = "foo bar 'hello world' baz";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hello world'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("baz"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Malformed) {
+ string input = "bar 'hello wo";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hello wo"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_Multiple) {
+ string input = "bar 'hel\"lo\" wo' baz\"";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'\"");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'hel\"lo\" wo'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("baz\""), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes) {
+ string input = "foo 'don\\'t do that'";
+ StringTokenizer t(input, " ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("'don\\'t do that'"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
+
+TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes2) {
+ string input = "foo='a, b', bar";
+ StringTokenizer t(input, ", ");
+ t.set_quote_chars("'");
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("foo='a, b'"), t.token());
+
+ EXPECT_TRUE(t.GetNext());
+ EXPECT_EQ(string("bar"), t.token());
+
+ EXPECT_FALSE(t.GetNext());
+}
diff --git a/base/string_util.cc b/base/string_util.cc
new file mode 100644
index 0000000..2122b9f
--- /dev/null
+++ b/base/string_util.cc
@@ -0,0 +1,1021 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// StringPrintf stuff based on strings/stringprintf.cc by Sanjay Ghemawat
+
+#include "base/string_util.h"
+
+#include <algorithm>
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/singleton.h"
+
+namespace {
+
+// Hack to convert any char-like type to its unsigned counterpart.
+// For example, it will convert char, signed char and unsigned char to unsigned
+// char.
+template<typename T>
+struct ToUnsigned {
+ typedef T Unsigned;
+};
+
+template<>
+struct ToUnsigned<char> {
+ typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<signed char> {
+ typedef unsigned char Unsigned;
+};
+template<>
+struct ToUnsigned<wchar_t> {
+ typedef unsigned short Unsigned;
+};
+template<>
+struct ToUnsigned<short> {
+ typedef unsigned short Unsigned;
+};
+
+// Used by ReplaceStringPlaceholders to track the position in the string of
+// replaced parameters.
+struct ReplacementOffset {
+ ReplacementOffset(int parameter, size_t offset)
+ : parameter(parameter),
+ offset(offset) {}
+
+ // Index of the parameter.
+ int parameter;
+
+ // Starting position in the string.
+ size_t offset;
+};
+
+static bool CompareParameter(const ReplacementOffset& elem1,
+ const ReplacementOffset& elem2) {
+ return elem1.parameter < elem2.parameter;
+}
+
+} // namespace
+
+
+const std::string& EmptyString() {
+ return *Singleton<std::string>::get();
+}
+
+const std::wstring& EmptyWString() {
+ return *Singleton<std::wstring>::get();
+}
+
+const wchar_t kWhitespaceWide[] = {
+ 0x0009, // <control-0009> to <control-000D>
+ 0x000A,
+ 0x000B,
+ 0x000C,
+ 0x000D,
+ 0x0020, // Space
+ 0x0085, // <control-0085>
+ 0x00A0, // No-Break Space
+ 0x1680, // Ogham Space Mark
+ 0x180E, // Mongolian Vowel Separator
+ 0x2000, // En Quad to Hair Space
+ 0x2001,
+ 0x2002,
+ 0x2003,
+ 0x2004,
+ 0x2005,
+ 0x2006,
+ 0x2007,
+ 0x2008,
+ 0x2009,
+ 0x200A,
+ 0x200C, // Zero Width Non-Joiner
+ 0x2028, // Line Separator
+ 0x2029, // Paragraph Separator
+ 0x202F, // Narrow No-Break Space
+ 0x205F, // Medium Mathematical Space
+ 0x3000, // Ideographic Space
+ 0
+};
+const char kWhitespaceASCII[] = {
+ 0x09, // <control-0009> to <control-000D>
+ 0x0A,
+ 0x0B,
+ 0x0C,
+ 0x0D,
+ 0x20, // Space
+ '\x85', // <control-0085>
+ '\xa0', // No-Break Space
+ 0
+};
+const char* const kCodepageUTF8 = "UTF-8";
+
+template<typename STR>
+TrimPositions TrimStringT(const STR& input,
+ const typename STR::value_type trim_chars[],
+ TrimPositions positions,
+ STR* output) {
+ // Find the edges of leading/trailing whitespace as desired.
+ const typename STR::size_type last_char = input.length() - 1;
+ const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ?
+ input.find_first_not_of(trim_chars) : 0;
+ const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ?
+ input.find_last_not_of(trim_chars) : last_char;
+
+ // When the string was all whitespace, report that we stripped off whitespace
+ // from whichever position the caller was interested in. For empty input, we
+ // stripped no whitespace, but we still need to clear |output|.
+ if (input.empty() ||
+ (first_good_char == STR::npos) || (last_good_char == STR::npos)) {
+ bool input_was_empty = input.empty(); // in case output == &input
+ output->clear();
+ return input_was_empty ? TRIM_NONE : positions;
+ }
+
+ // Trim the whitespace.
+ *output =
+ input.substr(first_good_char, last_good_char - first_good_char + 1);
+
+ // Return where we trimmed from.
+ return static_cast<TrimPositions>(
+ ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) |
+ ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
+}
+
+bool TrimString(const std::wstring& input,
+ wchar_t trim_chars[],
+ std::wstring* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+bool TrimString(const std::string& input,
+ char trim_chars[],
+ std::string* output) {
+ return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
+}
+
+TrimPositions TrimWhitespace(const std::wstring& input,
+ TrimPositions positions,
+ std::wstring* output) {
+ return TrimStringT(input, kWhitespaceWide, positions, output);
+}
+
+TrimPositions TrimWhitespace(const std::string& input,
+ TrimPositions positions,
+ std::string* output) {
+ return TrimStringT(input, kWhitespaceASCII, positions, output);
+}
+
+std::wstring CollapseWhitespace(const std::wstring& text,
+ bool trim_sequences_with_line_breaks) {
+ std::wstring result;
+ result.resize(text.size());
+
+ // Set flags to pretend we're already in a trimmed whitespace sequence, so we
+ // will trim any leading whitespace.
+ bool in_whitespace = true;
+ bool already_trimmed = true;
+
+ int chars_written = 0;
+ for (std::wstring::const_iterator i(text.begin()); i != text.end(); ++i) {
+ if (IsWhitespace(*i)) {
+ if (!in_whitespace) {
+ // Reduce all whitespace sequences to a single space.
+ in_whitespace = true;
+ result[chars_written++] = L' ';
+ }
+ if (trim_sequences_with_line_breaks && !already_trimmed &&
+ ((*i == '\n') || (*i == '\r'))) {
+ // Whitespace sequences containing CR or LF are eliminated entirely.
+ already_trimmed = true;
+ --chars_written;
+ }
+ } else {
+ // Non-whitespace chracters are copied straight across.
+ in_whitespace = false;
+ already_trimmed = false;
+ result[chars_written++] = *i;
+ }
+ }
+
+ if (in_whitespace && !already_trimmed) {
+ // Any trailing whitespace is eliminated.
+ --chars_written;
+ }
+
+ result.resize(chars_written);
+ return result;
+}
+
+std::string WideToASCII(const std::wstring& wide) {
+ DCHECK(IsStringASCII(wide));
+ return std::string(wide.begin(), wide.end());
+}
+
+std::wstring ASCIIToWide(const std::string& ascii) {
+ DCHECK(IsStringASCII(ascii));
+ return std::wstring(ascii.begin(), ascii.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;
+}
+
+bool IsString8Bit(const std::wstring& str) {
+ for (size_t i = 0; i < str.length(); i++) {
+ if (str[i] > 255)
+ return false;
+ }
+ return true;
+}
+
+bool IsStringASCII(const std::wstring& str) {
+ for (size_t i = 0; i < str.length(); i++) {
+ if (str[i] > 0x7F)
+ return false;
+ }
+ return true;
+}
+
+bool IsStringASCII(const std::string& str) {
+ for (size_t i = 0; i < str.length(); i++) {
+ if (static_cast<unsigned char>(str[i]) > 0x7F)
+ return false;
+ }
+ return true;
+}
+
+// Helper functions that determine whether the given character begins a
+// UTF-8 sequence of bytes with the given length. A character satisfies
+// "IsInUTF8Sequence" if it is anything but the first byte in a multi-byte
+// character.
+static inline bool IsBegin2ByteUTF8(int c) {
+ return (c & 0xE0) == 0xC0;
+}
+static inline bool IsBegin3ByteUTF8(int c) {
+ return (c & 0xF0) == 0xE0;
+}
+static inline bool IsBegin4ByteUTF8(int c) {
+ return (c & 0xF8) == 0xF0;
+}
+static inline bool IsInUTF8Sequence(int c) {
+ return (c & 0xC0) == 0x80;
+}
+
+// This function was copied from Mozilla, with modifications. The original code
+// was 'IsUTF8' in xpcom/string/src/nsReadableUtils.cpp. The license block for
+// this function is:
+// This function subject to the Mozilla Public License Version
+// 1.1 (the "License"); you may not use this code except in compliance with
+// the License. You may obtain a copy of the License at
+// http://www.mozilla.org/MPL/
+//
+// Software distributed under the License is distributed on an "AS IS" basis,
+// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+// for the specific language governing rights and limitations under the
+// License.
+//
+// The Original Code is mozilla.org code.
+//
+// The Initial Developer of the Original Code is
+// Netscape Communications Corporation.
+// Portions created by the Initial Developer are Copyright (C) 2000
+// the Initial Developer. All Rights Reserved.
+//
+// Contributor(s):
+// Scott Collins <scc@mozilla.org> (original author)
+//
+// This is a template so that it can be run on wide and 8-bit strings. We want
+// to run it on wide strings when we have input that we think may have
+// originally been UTF-8, but has been converted to wide characters because
+// that's what we (and Windows) use internally.
+template<typename CHAR>
+static bool IsStringUTF8T(const CHAR* str) {
+ bool overlong = false;
+ bool surrogate = false;
+ bool nonchar = false;
+
+ // overlong byte upper bound
+ typename ToUnsigned<CHAR>::Unsigned olupper = 0;
+
+ // surrogate byte lower bound
+ typename ToUnsigned<CHAR>::Unsigned slower = 0;
+
+ // incremented when inside a multi-byte char to indicate how many bytes
+ // are left in the sequence
+ int positions_left = 0;
+
+ for (int i = 0; str[i] != 0; i++) {
+ // This whole function assume an unsigned value so force its conversion to
+ // an unsigned value.
+ typename ToUnsigned<CHAR>::Unsigned c = str[i];
+ if (c < 0x80)
+ continue; // ASCII
+
+ if (c <= 0xC1) {
+ // [80-BF] where not expected, [C0-C1] for overlong
+ return false;
+ } else if (IsBegin2ByteUTF8(c)) {
+ positions_left = 1;
+ } else if (IsBegin3ByteUTF8(c)) {
+ positions_left = 2;
+ if (c == 0xE0) {
+ // to exclude E0[80-9F][80-BF]
+ overlong = true;
+ olupper = 0x9F;
+ } else if (c == 0xED) {
+ // ED[A0-BF][80-BF]: surrogate codepoint
+ surrogate = true;
+ slower = 0xA0;
+ } else if (c == 0xEF) {
+ // EF BF [BE-BF] : non-character
+ nonchar = true;
+ }
+ } else if (c <= 0xF4) {
+ positions_left = 3;
+ nonchar = true;
+ if (c == 0xF0) {
+ // to exclude F0[80-8F][80-BF]{2}
+ overlong = true;
+ olupper = 0x8F;
+ } else if (c == 0xF4) {
+ // to exclude F4[90-BF][80-BF]
+ // actually not surrogates but codepoints beyond 0x10FFFF
+ surrogate = true;
+ slower = 0x90;
+ }
+ } else {
+ return false;
+ }
+
+ // eat the rest of this multi-byte character
+ while (positions_left) {
+ positions_left--;
+ i++;
+ c = str[i];
+ if (!c)
+ return false; // end of string but not end of character sequence
+
+ // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF]
+ if (nonchar && (!positions_left && c < 0xBE ||
+ positions_left == 1 && c != 0xBF ||
+ positions_left == 2 && 0x0F != (0x0F & c) )) {
+ nonchar = false;
+ }
+ if (!IsInUTF8Sequence(c) || overlong && c <= olupper ||
+ surrogate && slower <= c || nonchar && !positions_left ) {
+ return false;
+ }
+ overlong = surrogate = false;
+ }
+ }
+ return true;
+}
+
+bool IsStringUTF8(const char* str) {
+ return IsStringUTF8T(str);
+}
+
+bool IsStringWideUTF8(const wchar_t* str) {
+ return IsStringUTF8T(str);
+}
+
+template<typename Iter>
+static inline bool DoLowerCaseEqualsASCII(Iter a_begin,
+ Iter a_end,
+ const char* b) {
+ for (Iter it = a_begin; it != a_end; ++it, ++b) {
+ if (!*b || ToLowerASCII(*it) != *b)
+ return false;
+ }
+ return *b == 0;
+}
+
+// Front-ends for LowerCaseEqualsASCII.
+bool LowerCaseEqualsASCII(const std::string& a, const char* b) {
+ return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+bool LowerCaseEqualsASCII(const std::wstring& a, const char* b) {
+ return DoLowerCaseEqualsASCII(a.begin(), a.end(), b);
+}
+
+bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+ std::string::const_iterator a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+ std::wstring::const_iterator a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+bool LowerCaseEqualsASCII(const char* a_begin,
+ const char* a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+ const wchar_t* a_end,
+ const char* b) {
+ return DoLowerCaseEqualsASCII(a_begin, a_end, b);
+}
+
+bool StartsWithASCII(const std::string& str,
+ const std::string& search,
+ bool case_sensitive) {
+ if (case_sensitive)
+ return str.compare(0, search.length(), search) == 0;
+ else
+ return StrNCaseCmp(str.c_str(), search.c_str(), search.length()) == 0;
+}
+
+DataUnits GetByteDisplayUnits(int64 bytes) {
+ // The byte thresholds at which we display amounts. A byte count is displayed
+ // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1].
+ // This must match the DataUnits enum.
+ static const int64 kUnitThresholds[] = {
+ 0, // DATA_UNITS_BYTE,
+ 3*1024, // DATA_UNITS_KILOBYTE,
+ 2*1024*1024, // DATA_UNITS_MEGABYTE,
+ 1024*1024*1024 // DATA_UNITS_GIGABYTE,
+ };
+
+ if (bytes < 0) {
+ NOTREACHED() << "Negative bytes value";
+ return DATA_UNITS_BYTE;
+ }
+
+ int unit_index = arraysize(kUnitThresholds);
+ while (--unit_index > 0) {
+ if (bytes >= kUnitThresholds[unit_index])
+ break;
+ }
+
+ DCHECK(unit_index >= DATA_UNITS_BYTE && unit_index <= DATA_UNITS_GIGABYTE);
+ return DataUnits(unit_index);
+}
+
+// TODO(mpcomplete): deal with locale
+// Byte suffixes. This must match the DataUnits enum.
+static const wchar_t* const kByteStrings[] = {
+ L"B",
+ L"kB",
+ L"MB",
+ L"GB"
+};
+
+static const wchar_t* const kSpeedStrings[] = {
+ L"B/s",
+ L"kB/s",
+ L"MB/s",
+ L"GB/s"
+};
+
+std::wstring FormatBytesInternal(int64 bytes,
+ DataUnits units,
+ bool show_units,
+ const wchar_t* const* suffix) {
+ if (bytes < 0) {
+ NOTREACHED() << "Negative bytes value";
+ return std::wstring();
+ }
+
+ DCHECK(units >= DATA_UNITS_BYTE && units <= DATA_UNITS_GIGABYTE);
+
+ // Put the quantity in the right units.
+ double unit_amount = static_cast<double>(bytes);
+ for (int i = 0; i < units; ++i)
+ unit_amount /= 1024.0;
+
+ wchar_t tmp[64];
+ // If the first decimal digit is 0, don't show it.
+ double int_part;
+ double fractional_part = modf(unit_amount, &int_part);
+ modf(fractional_part * 10, &int_part);
+ if (int_part == 0)
+ SWPrintF(tmp, arraysize(tmp), L"%lld", static_cast<int64>(unit_amount));
+ else
+ SWPrintF(tmp, arraysize(tmp), L"%.1lf", unit_amount);
+
+ std::wstring ret(tmp);
+ if (show_units) {
+ ret += L" ";
+ ret += suffix[units];
+ }
+
+ return ret;
+}
+
+std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units) {
+ return FormatBytesInternal(bytes, units, show_units, kByteStrings);
+}
+
+std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units) {
+ return FormatBytesInternal(bytes, units, show_units, kSpeedStrings);
+}
+
+template<class StringType>
+void DoReplaceSubstringsAfterOffset(StringType* str,
+ typename StringType::size_type start_offset,
+ const StringType& find_this,
+ const StringType& replace_with) {
+ if ((start_offset == StringType::npos) || (start_offset >= str->length()))
+ return;
+
+ DCHECK(!find_this.empty());
+ for (typename StringType::size_type offs(str->find(find_this, start_offset));
+ offs != StringType::npos; offs = str->find(find_this, offs)) {
+ str->replace(offs, find_this.length(), replace_with);
+ offs += replace_with.length();
+ }
+}
+
+void ReplaceSubstringsAfterOffset(std::wstring* str,
+ std::wstring::size_type start_offset,
+ const std::wstring& find_this,
+ const std::wstring& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with);
+}
+
+void ReplaceSubstringsAfterOffset(std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with) {
+ DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with);
+}
+
+// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter
+// is the size of the buffer. These return the number of characters in the
+// formatted string excluding the NUL terminator, or if the buffer is not
+// large enough to accommodate the formatted string without truncation, the
+// number of characters that would be in the fully-formatted string.
+inline int vsnprintfT(char* buffer,
+ size_t buf_size,
+ const char* format,
+ va_list argptr) {
+ return VSNPrintF(buffer, buf_size, format, argptr);
+}
+
+inline int vsnprintfT(wchar_t* buffer,
+ size_t buf_size,
+ const wchar_t* format,
+ va_list argptr) {
+ return VSWPrintF(buffer, buf_size, format, argptr);
+}
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class char_type>
+static void StringAppendVT(
+ std::basic_string<char_type, std::char_traits<char_type> >* dst,
+ const char_type* format,
+ va_list ap) {
+
+ // First try with a small fixed size buffer.
+ // This buffer size should be kept in sync with StringUtilTest.GrowBoundary.
+ const int kStackLength = 1024;
+ char_type stack_buf[kStackLength];
+
+ // It's possible for methods that use a va_list to invalidate the data in it
+ // upon use. The fix is to make a copy of the structure before using it and
+ // use that copy instead. It is not guaranteed that assignment is a copy, and
+ // va_copy is not supported by VC, so the UnitTest tests this capability.
+ va_list backup_ap = ap;
+ int result = vsnprintfT(stack_buf, kStackLength, format, backup_ap);
+ va_end(backup_ap);
+
+ if (result >= 0 && result < kStackLength) {
+ // It fit.
+ dst->append(stack_buf, result);
+ return;
+ }
+
+ int mem_length = result;
+
+ // vsnprintfT may have failed for some reason other than an insufficient
+ // buffer, such as an invalid characer. Check that the requested buffer
+ // size is smaller than what was already attempted
+ if (mem_length < 0 || mem_length < kStackLength) {
+ DLOG(WARNING) << "Unable to compute size of the requested string.";
+ return;
+ }
+
+ mem_length++; // Include the NULL terminator.
+ scoped_ptr<char_type> mem_buf(new char_type[mem_length]);
+
+ // Do the printf.
+ result = vsnprintfT(mem_buf.get(), mem_length, format, ap);
+ DCHECK(result < mem_length);
+ if (result < 0) {
+ DLOG(WARNING) << "Unable to printf the requested string.";
+ return;
+ }
+
+ dst->append(mem_buf.get(), result);
+}
+
+std::string Uint64ToString(uint64 value) {
+ return StringPrintf("%llu", value);
+}
+
+std::string Int64ToString(int64 value) {
+ return StringPrintf("%I64d", value);
+}
+
+std::wstring Int64ToWString(int64 value) {
+ return StringPrintf(L"%I64d", value);
+}
+
+std::string IntToString(int value) {
+ return StringPrintf("%d", value);
+}
+
+std::wstring IntToWString(int value) {
+ return StringPrintf(L"%d", value);
+}
+
+inline void StringAppendV(std::string* dst, const char* format, va_list ap) {
+ StringAppendVT<char>(dst, format, ap);
+}
+
+inline void StringAppendV(std::wstring* dst,
+ const wchar_t* format,
+ va_list ap) {
+ StringAppendVT<wchar_t>(dst, format, ap);
+}
+
+std::string StringPrintf(const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::string result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+std::wstring StringPrintf(const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ std::wstring result;
+ StringAppendV(&result, format, ap);
+ va_end(ap);
+ return result;
+}
+
+const std::string& SStringPrintf(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ dst->clear();
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+ return *dst;
+}
+
+void StringAppendF(std::string* dst, const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ StringAppendV(dst, format, ap);
+ va_end(ap);
+}
+
+template<typename STR>
+static void SplitStringT(const STR& str,
+ const typename STR::value_type s,
+ bool trim_whitespace,
+ std::vector<STR>* r) {
+ size_t last = 0;
+ size_t i;
+ size_t c = str.size();
+ for (i = 0; i <= c; ++i) {
+ if (i == c || str[i] == s) {
+ size_t len = i - last;
+ STR tmp = str.substr(last, len);
+ if (trim_whitespace) {
+ STR t_tmp;
+ TrimWhitespace(tmp, TRIM_ALL, &t_tmp);
+ r->push_back(t_tmp);
+ } else {
+ r->push_back(tmp);
+ }
+ last = i + 1;
+ }
+ }
+}
+
+void SplitString(const std::wstring& str,
+ wchar_t s,
+ std::vector<std::wstring>* r) {
+ SplitStringT(str, s, true, r);
+}
+
+void SplitString(const std::string& str,
+ char s,
+ std::vector<std::string>* r) {
+ SplitStringT(str, s, true, r);
+}
+
+void SplitStringDontTrim(const std::wstring& str,
+ wchar_t s,
+ std::vector<std::wstring>* r) {
+ SplitStringT(str, s, false, r);
+}
+
+void SplitStringDontTrim(const std::string& str,
+ char s,
+ std::vector<std::string>* r) {
+ SplitStringT(str, s, false, r);
+}
+
+void SplitStringAlongWhitespace(const std::wstring& str,
+ std::vector<std::wstring>* result) {
+ const size_t length = str.length();
+ if (!length)
+ return;
+
+ bool last_was_ws = false;
+ size_t last_non_ws_start = 0;
+ for (size_t i = 0; i < length; ++i) {
+ switch(str[i]) {
+ // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR.
+ case L' ':
+ case L'\t':
+ case L'\xA':
+ case L'\xB':
+ case L'\xC':
+ case L'\xD':
+ if (!last_was_ws) {
+ if (i > 0) {
+ result->push_back(
+ str.substr(last_non_ws_start, i - last_non_ws_start));
+ }
+ last_was_ws = true;
+ }
+ break;
+
+ default: // Not a space character.
+ if (last_was_ws) {
+ last_was_ws = false;
+ last_non_ws_start = i;
+ }
+ break;
+ }
+ }
+ if (!last_was_ws) {
+ result->push_back(
+ str.substr(last_non_ws_start, length - last_non_ws_start));
+ }
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ size_t* offset) {
+ std::vector<size_t> offsets;
+ std::wstring result = ReplaceStringPlaceholders(format_string, a,
+ std::wstring(),
+ std::wstring(),
+ std::wstring(), &offsets);
+ DCHECK(offsets.size() == 1);
+ if (offset) {
+ *offset = offsets[0];
+ }
+ return result;
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ std::vector<size_t>* offsets) {
+ return ReplaceStringPlaceholders(format_string, a, b, std::wstring(),
+ std::wstring(), offsets);
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ const std::wstring& c,
+ std::vector<size_t>* offsets) {
+ return ReplaceStringPlaceholders(format_string, a, b, c, std::wstring(),
+ offsets);
+}
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ const std::wstring& c,
+ const std::wstring& d,
+ std::vector<size_t>* offsets) {
+ // We currently only support up to 4 place holders ($1 through $4), although
+ // it's easy enough to add more.
+ const std::wstring* subst_texts[] = { &a, &b, &c, &d };
+
+ std::wstring formatted;
+ formatted.reserve(format_string.length() + a.length() +
+ b.length() + c.length() + d.length());
+
+ std::vector<ReplacementOffset> r_offsets;
+
+ // Replace $$ with $ and $1-$4 with placeholder text if it exists.
+ for (std::wstring::const_iterator i = format_string.begin();
+ i != format_string.end(); ++i) {
+ if ('$' == *i) {
+ if (i + 1 != format_string.end()) {
+ ++i;
+ DCHECK('$' == *i || ('1' <= *i && *i <= '4')) <<
+ "Invalid placeholder: " << *i;
+ if ('$' == *i) {
+ formatted.push_back('$');
+ } else {
+ int index = *i - '1';
+ if (offsets) {
+ ReplacementOffset r_offset(index,
+ static_cast<int>(formatted.size()));
+ r_offsets.insert(std::lower_bound(r_offsets.begin(),
+ r_offsets.end(), r_offset,
+ &CompareParameter),
+ r_offset);
+ }
+ formatted.append(*subst_texts[index]);
+ }
+ }
+ } else {
+ formatted.push_back(*i);
+ }
+ }
+ if (offsets) {
+ for (std::vector<ReplacementOffset>::const_iterator i = r_offsets.begin();
+ i != r_offsets.end(); ++i) {
+ offsets->push_back(i->offset);
+ }
+ }
+ return formatted;
+}
+
+template <class CHAR>
+static bool IsWildcard(CHAR character) {
+ return character == '*' || character == '?';
+}
+
+// Move the strings pointers to the point where they start to differ.
+template <class CHAR>
+static void EatSameChars(const CHAR** pattern, const CHAR** string) {
+ bool escaped = false;
+ while (**pattern && **string) {
+ if (!escaped && IsWildcard(**pattern)) {
+ // We don't want to match wildcard here, except if it's escaped.
+ return;
+ }
+
+ // Check if the escapement char is found. If so, skip it and move to the
+ // next character.
+ if (!escaped && **pattern == L'\\') {
+ escaped = true;
+ (*pattern)++;
+ continue;
+ }
+
+ // Check if the chars match, if so, increment the ptrs.
+ if (**pattern == **string) {
+ (*pattern)++;
+ (*string)++;
+ } else {
+ // Uh ho, it did not match, we are done. If the last char was an
+ // escapement, that means that it was an error to advance the ptr here,
+ // let's put it back where it was. This also mean that the MatchPattern
+ // function will return false because if we can't match an escape char
+ // here, then no one will.
+ if (escaped) {
+ (*pattern)--;
+ }
+ return;
+ }
+
+ escaped = false;
+ }
+}
+
+template <class CHAR>
+static void EatWildcard(const CHAR** pattern) {
+ while(**pattern) {
+ if (!IsWildcard(**pattern))
+ return;
+ (*pattern)++;
+ }
+}
+
+template <class CHAR>
+static bool MatchPatternT(const CHAR* eval, const CHAR* pattern) {
+ // Eat all the matching chars.
+ EatSameChars(&pattern, &eval);
+
+ // If the string is empty, then the pattern must be empty too, or contains
+ // only wildcards.
+ if (*eval == 0) {
+ EatWildcard(&pattern);
+ if (*pattern)
+ return false;
+ return true;
+ }
+
+ // Pattern is empty but not string, this is not a match.
+ if (*pattern == 0)
+ return false;
+
+ // If this is a question mark, then we need to compare the rest with
+ // the current string or the string with one character eaten.
+ if (pattern[0] == '?') {
+ if (MatchPatternT(eval, pattern + 1) ||
+ MatchPatternT(eval + 1, pattern + 1))
+ return true;
+ }
+
+ // This is a *, try to match all the possible substrings with the remainder
+ // of the pattern.
+ if (pattern[0] == '*') {
+ while (*eval) {
+ if (MatchPatternT(eval, pattern + 1))
+ return true;
+ eval++;
+ }
+
+ // We reached the end of the string, let see if the pattern contains only
+ // wildcards.
+ if (*eval == 0) {
+ EatWildcard(&pattern);
+ if (*pattern)
+ return false;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool MatchPattern(const std::wstring& eval, const std::wstring& pattern) {
+ return MatchPatternT(eval.c_str(), pattern.c_str());
+}
+
+bool MatchPattern(const std::string& eval, const std::string& pattern) {
+ return MatchPatternT(eval.c_str(), pattern.c_str());
+}
diff --git a/base/string_util.h b/base/string_util.h
new file mode 100644
index 0000000..e5fd147
--- /dev/null
+++ b/base/string_util.h
@@ -0,0 +1,504 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// This file defines utility functions for working with strings.
+
+#ifndef BASE_STRING_UTIL_H__
+#define BASE_STRING_UTIL_H__
+
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+// Safe standard library wrappers for all platforms. The Str* variants
+// operate on NUL-terminated char* strings, like the standard library's str*
+// functions.
+
+// Copy at most (dst_size - 1) characters from src to dest, guaranteeing dst
+// will be NUL-terminated. If the string is copied without truncation,
+// returns true. dst is undefined if the string cannot be copied without
+// truncation, and the function will either return false or cause termination.
+bool StrCpy(char* dest, const char* src, size_t dst_size);
+
+// As with StrCpy, but copies at most the minimum of (dst_size - 1) and
+// src_size characters.
+bool StrNCpy(char* dest, const char* src, size_t dst_size, size_t src_size);
+
+// Compare up to count characters of s1 and s2 without regard to case using
+// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if
+// s2 > s1 according to a lexicographic comparison.
+int StrNCaseCmp(const char* s1, const char* s2, size_t count);
+
+// Wrapper for vsnprintf, snprintf that always NUL-terminates and always
+// returns the number of characters that would be in an untruncated formatted
+// string, even when truncation occurs.
+int VSNPrintF(char* buffer, size_t size,
+ const char* format, va_list arguments);
+int SNPrintF(char* buffer, size_t size, const char* format, ...);
+
+// The Wcs* variants operate on NUL-terminated wchar_t* strings, like the
+// standard library's wcs* functions. Otherwise, these behave the same as
+// the Str* variants above.
+
+bool WcsCpy(wchar_t* dest, const wchar_t* src, size_t dst_size);
+bool WcsNCpy(wchar_t* dest, const wchar_t* src, size_t dst_size);
+
+int VSWPrintF(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments);
+int SWPrintF(wchar_t* buffer, size_t size, const wchar_t* format, ...);
+
+// Some of these implementations need to be inlined.
+
+#if defined(WIN32)
+#include "base/string_util_win.h"
+#elif defined(__APPLE__)
+#include "base/string_util_mac.h"
+#else
+#error Define string operations appropriately for your platform
+#endif
+
+inline int SNPrintF(char* buffer, size_t size, const char* format, ...) {
+ va_list arguments;
+ va_start(arguments, format);
+ int result = VSNPrintF(buffer, size, format, arguments);
+ va_end(arguments);
+ return result;
+}
+
+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;
+}
+
+// Returns a reference to a globally unique empty string that functions can
+// return. Use this to avoid static construction of strings, not to replace
+// any and all uses of "std::string()" as nicer-looking sugar.
+// These functions are threadsafe.
+const std::string& EmptyString();
+const std::wstring& EmptyWString();
+
+extern const wchar_t kWhitespaceWide[];
+extern const char kWhitespaceASCII[];
+
+// Names of codepages (charsets) understood by icu.
+extern const char* const kCodepageUTF8;
+
+// Removes characters in trim_chars from the beginning and end of input.
+// NOTE: Safe to use the same variable for both input and output.
+bool TrimString(const std::wstring& input,
+ wchar_t trim_chars[],
+ std::wstring* output);
+bool TrimString(const std::string& input,
+ char trim_chars[],
+ std::string* output);
+
+// Trims any whitespace from either end of the input string. Returns where
+// whitespace was found. The non-wide version of this function only looks for
+// ASCII whitespace; UTF-8 code-points are not searched for (use the wide
+// version instead).
+// NOTE: Safe to use the same variable for both input and output.
+enum TrimPositions {
+ TRIM_NONE = 0,
+ TRIM_LEADING = 1 << 0,
+ TRIM_TRAILING = 1 << 1,
+ TRIM_ALL = TRIM_LEADING | TRIM_TRAILING,
+};
+TrimPositions TrimWhitespace(const std::wstring& input,
+ TrimPositions positions,
+ std::wstring* output);
+TrimPositions TrimWhitespace(const std::string& input,
+ TrimPositions positions,
+ std::string* output);
+
+// Searches for CR or LF characters. Removes all contiguous whitespace
+// strings that contain them. This is useful when trying to deal with text
+// copied from terminals.
+// Returns |text, with the following three transformations:
+// (1) Leading and trailing whitespace is trimmed.
+// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace
+// sequences containing a CR or LF are trimmed.
+// (3) All other whitespace sequences are converted to single spaces.
+std::wstring CollapseWhitespace(const std::wstring& text,
+ bool trim_sequences_with_line_breaks);
+
+// These convert between ASCII (7-bit) and UTF16 strings.
+std::string WideToASCII(const std::wstring& wide);
+std::wstring ASCIIToWide(const std::string& ascii);
+
+// These convert between UTF8 and UTF16 strings. They are potentially slow,
+// so avoid unnecessary conversions. Most things should be in UTF16.
+std::string WideToUTF8(const std::wstring& wide);
+std::wstring UTF8ToWide(const std::string& utf8);
+
+// Converts between wide strings and whatever the native multibyte encoding
+// is. The native multibyte encoding on English machines will often Latin-1,
+// but could be ShiftJIS or even UTF-8, among others.
+//
+// These functions can be dangerous. Do not use unless you are sure you are
+// giving them to/getting them from somebody who expects the current platform
+// 8-bit encoding.
+std::string WideToNativeMB(const std::wstring& wide);
+std::wstring NativeMBToWide(const std::string& native_mb);
+
+// Defines the error handling modes of WideToCodepage and CodepageToWide.
+class OnStringUtilConversionError {
+ public:
+ enum Type {
+ // The function will return failure. The output buffer will be empty.
+ FAIL,
+
+ // The offending characters are skipped and the conversion will proceed as
+ // if they did not exist.
+ SKIP,
+ };
+
+ private:
+ OnStringUtilConversionError();
+};
+
+// Converts between wide strings and the encoding specified. If the
+// encoding doesn't exist or the encoding fails (when on_error is FAIL),
+// returns false.
+bool WideToCodepage(const std::wstring& wide,
+ const char* codepage_name,
+ OnStringUtilConversionError::Type on_error,
+ std::string* encoded);
+bool CodepageToWide(const std::string& encoded,
+ const char* codepage_name,
+ OnStringUtilConversionError::Type on_error,
+ std::wstring* wide);
+
+// Converts the given wide string to the corresponding Latin1. This will fail
+// (return false) if any characters are more than 255.
+bool WideToLatin1(const std::wstring& wide, std::string* latin1);
+
+// Returns true if the specified string matches the criteria. How can a wide
+// string be 8-bit or UTF8? It contains only characters that are < 256 (in the
+// first case) or characters that use only 8-bits and whose 8-bit
+// representation looks like a UTF-8 string (the second case).
+bool IsString8Bit(const std::wstring& str);
+bool IsStringUTF8(const char* str);
+bool IsStringWideUTF8(const wchar_t* str);
+bool IsStringASCII(const std::wstring& str);
+bool IsStringASCII(const std::string& str);
+
+// ASCII-specific tolower. The standard library's tolower is locale sensitive,
+// so we don't want to use it here.
+template <class Char> inline Char ToLowerASCII(Char c) {
+ return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c;
+}
+
+// Converts the elements of the given string. This version uses a pointer to
+// clearly differentiate it from the non-pointer variant.
+template <class str> inline void StringToLowerASCII(str* s) {
+ for (typename str::iterator i = s->begin(); i != s->end(); ++i)
+ *i = ToLowerASCII(*i);
+}
+
+template <class str> inline str StringToLowerASCII(const str& s) {
+ // for std::string and std::wstring
+ str output(s);
+ StringToLowerASCII(&output);
+ return output;
+}
+
+// Compare the lower-case form of the given string against the given ASCII
+// string. This is useful for doing checking if an input string matches some
+// token, and it is optimized to avoid intermediate string copies. This API is
+// borrowed from the equivalent APIs in Mozilla.
+bool LowerCaseEqualsASCII(const std::string& a, const char* b);
+bool LowerCaseEqualsASCII(const std::wstring& a, const char* b);
+
+// Same thing, but with string iterators instead.
+bool LowerCaseEqualsASCII(std::string::const_iterator a_begin,
+ std::string::const_iterator a_end,
+ const char* b);
+bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin,
+ std::wstring::const_iterator a_end,
+ const char* b);
+bool LowerCaseEqualsASCII(const char* a_begin,
+ const char* a_end,
+ const char* b);
+bool LowerCaseEqualsASCII(const wchar_t* a_begin,
+ const wchar_t* a_end,
+ const char* b);
+
+// Returns true if str starts with search, or false otherwise.
+// This only works on ASCII strings.
+bool StartsWithASCII(const std::string& str,
+ const std::string& search,
+ bool case_sensitive);
+
+// Determines the type of ASCII character, independent of locale (the C
+// library versions will change based on locale).
+template <typename Char>
+inline bool IsAsciiWhitespace(Char c) {
+ return c == ' ' || c == '\r' || c == '\n' || c == '\t';
+}
+template <typename Char>
+inline bool IsAsciiAlpha(Char c) {
+ return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'));
+}
+template <typename Char>
+inline bool IsAsciiDigit(Char c) {
+ return c >= '0' && c <= '9';
+}
+
+// Returns true if it's a whitespace character.
+inline bool IsWhitespace(wchar_t c) {
+ return wcschr(kWhitespaceWide, c) != NULL;
+}
+
+// TODO(mpcomplete): Decide if we should change these names to KIBI, etc,
+// or if we should actually use metric units, or leave as is.
+enum DataUnits {
+ DATA_UNITS_BYTE = 0,
+ DATA_UNITS_KILOBYTE,
+ DATA_UNITS_MEGABYTE,
+ DATA_UNITS_GIGABYTE,
+};
+
+// Return the unit type that is appropriate for displaying the amount of bytes
+// passed in.
+DataUnits GetByteDisplayUnits(int64 bytes);
+
+// Return a byte string in human-readable format, displayed in units appropriate
+// specified by 'units', with an optional unit suffix.
+// Ex: FormatBytes(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB"
+// Ex: FormatBytes(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1"
+std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units);
+
+// As above, but with "/s" units.
+// Ex: FormatSpeed(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB/s"
+// Ex: FormatSpeed(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1"
+std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units);
+
+// Return a number formated with separators in the user's locale way.
+// Ex: FormatNumber(1234567) => 1,234,567
+std::wstring FormatNumber(int64 number);
+
+// Starting at |start_offset| (usually 0), look through |str| and replace all
+// instances of |find_this| with |replace_with|.
+//
+// This does entire substrings; use std::replace in <algorithm> for single
+// characters, for example:
+// std::replace(str.begin(), str.end(), 'a', 'b');
+void ReplaceSubstringsAfterOffset(std::wstring* str,
+ std::wstring::size_type start_offset,
+ const std::wstring& find_this,
+ const std::wstring& replace_with);
+void ReplaceSubstringsAfterOffset(std::string* str,
+ std::string::size_type start_offset,
+ const std::string& find_this,
+ const std::string& replace_with);
+
+// Specialized string-conversion functions.
+std::string Uint64ToString(uint64 value);
+std::string IntToString(int value);
+std::string Int64ToString(int64 value);
+std::wstring Int64ToWString(int64 value);
+std::wstring IntToWString(int value);
+int64 StringToInt64(const std::string& value);
+int64 StringToInt64(const std::wstring& value);
+
+// Return a C++ string given printf-like input.
+std::string StringPrintf(const char* format, ...);
+std::wstring StringPrintf(const wchar_t* format, ...);
+
+// Store result into a supplied string and return it
+const std::string& SStringPrintf(std::string* dst, const char* format, ...);
+const std::wstring& SStringPrintf(std::wstring* dst,
+ const wchar_t* format, ...);
+
+// Append result to a supplied string
+void StringAppendF(std::string* dst, const char* format, ...);
+void StringAppendF(std::wstring* dst, const wchar_t* format, ...);
+
+// Lower-level routine that takes a va_list and appends to a specified
+// string. All other routines are just convenience wrappers around it.
+void StringAppendV(std::string* dst, const char* format, va_list ap);
+void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap);
+
+// This is mpcomplete's pattern for saving a string copy when dealing with
+// a function that writes results into a wchar_t[] and wanting the result to
+// end up in a std::wstring. It ensures that the std::wstring's internal
+// buffer has enough room to store the characters to be written into it, and
+// sets its .length() attribute to the right value.
+//
+// The reserve() call allocates the memory required to hold the string
+// plus a terminating null. This is done because resize() isn't
+// guaranteed to reserve space for the null. The resize() call is
+// simply the only way to change the string's 'length' member.
+//
+// XXX-performance: the call to wide.resize() takes linear time, since it fills
+// the string's buffer with nulls. I call it to change the length of the
+// string (needed because writing directly to the buffer doesn't do this).
+// Perhaps there's a constant-time way to change the string's length.
+template <class char_type>
+inline char_type* WriteInto(
+ std::basic_string<char_type, std::char_traits<char_type>,
+ std::allocator<char_type> >* str,
+ size_t length_including_null) {
+ str->reserve(length_including_null);
+ str->resize(length_including_null - 1);
+ return &((*str)[0]);
+}
+
+//-----------------------------------------------------------------------------
+// CharTraits is provides wrappers with common function names for char/wchar_t
+// specific CRT functions.
+
+template <class CharT> struct CharTraits {
+};
+
+template <>
+struct CharTraits<char> {
+ static inline size_t length(const char* s) {
+ return strlen(s);
+ }
+ static inline bool copy(char* dst, size_t dst_size, const char* s) {
+ return StrCpy(dst, s, dst_size);
+ }
+ static inline bool copy_num(char* dst, size_t dst_size, const char* s,
+ size_t s_len) {
+ if (dst_size < (s_len + 1))
+ return false;
+ memcpy(dst, s, s_len);
+ dst[s_len] = '\0';
+ return true;
+ }
+};
+
+template <>
+struct CharTraits<wchar_t> {
+ static inline size_t length(const wchar_t* s) {
+ return wcslen(s);
+ }
+ static inline bool copy(wchar_t* dst, size_t dst_size, const wchar_t* s) {
+ return WcsCpy(dst, s, dst_size);
+ }
+ static inline bool copy_num(wchar_t* dst, size_t dst_size, const wchar_t* s,
+ size_t s_len) {
+ if (dst_size < (s_len + 1))
+ return false;
+ memcpy(dst, s, s_len * sizeof(wchar_t));
+ dst[s_len] = '\0';
+ return true;
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+// Function objects to aid in comparing/searching strings.
+
+template<typename Char> struct CaseInsensitiveCompare {
+ public:
+ bool operator()(Char x, Char y) const {
+ return tolower(x) == tolower(y);
+ }
+};
+
+template<typename Char> struct CaseInsensitiveCompareASCII {
+ public:
+ bool operator()(Char x, Char y) const {
+ return ToLowerASCII(x) == ToLowerASCII(y);
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+// Splits |str| into a vector of strings delimited by |s|. Append the results
+// into |r| as they appear. If several instances of |s| are contiguous, or if
+// |str| begins with or ends with |s|, then an empty string is inserted.
+//
+// Every substring is trimmed of any leading or trailing white space.
+void SplitString(const std::wstring& str,
+ wchar_t s,
+ std::vector<std::wstring>* r);
+void SplitString(const std::string& str,
+ char s,
+ std::vector<std::string>* r);
+
+// The same as SplitString, but don't trim white space.
+void SplitStringDontTrim(const std::wstring& str,
+ wchar_t s,
+ std::vector<std::wstring>* r);
+void SplitStringDontTrim(const std::string& str,
+ char s,
+ std::vector<std::string>* r);
+
+// WARNING: this uses whitespace as defined by the HTML5 spec. If you need
+// a function similar to this but want to trim all types of whitespace, then
+// factor this out into a function that takes a string containing the characters
+// that are treated as whitespace.
+//
+// Splits the string along whitespace (where whitespace is the five space
+// characters defined by HTML 5). Each contiguous block of non-whitespace
+// characters is added to result.
+void SplitStringAlongWhitespace(const std::wstring& str,
+ std::vector<std::wstring>* result);
+
+// Replace $1-$2-$3 in the format string with |a| and |b| respectively.
+// Additionally, $$ is replaced by $. The offset/offsets parameter here can be
+// NULL.
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ size_t* offset);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ std::vector<size_t>* offsets);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ const std::wstring& c,
+ std::vector<size_t>* offsets);
+
+std::wstring ReplaceStringPlaceholders(const std::wstring& format_string,
+ const std::wstring& a,
+ const std::wstring& b,
+ const std::wstring& c,
+ const std::wstring& d,
+ std::vector<size_t>* offsets);
+
+// Returns true if the string passed in matches the pattern. The pattern
+// string can contain wildcards like * and ?
+// TODO(iyengar) This function may not work correctly for CJK strings as
+// it does individual character matches.
+// The backslash character (\) is an escape character for * and ?
+bool MatchPattern(const std::wstring& string, const std::wstring& pattern);
+bool MatchPattern(const std::string& string, const std::string& pattern);
+
+#endif // BASE_STRING_UTIL_H__
diff --git a/base/string_util_icu.cc b/base/string_util_icu.cc
new file mode 100644
index 0000000..797ccbd
--- /dev/null
+++ b/base/string_util_icu.cc
@@ -0,0 +1,201 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include "base/string_util.h"
+
+#include <string.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/singleton.h"
+#include "unicode/ucnv.h"
+#include "unicode/numfmt.h"
+#include "unicode/ustring.h"
+
+// Codepage <-> Wide -----------------------------------------------------------
+
+// Convert a unicode string into the specified codepage_name. If the codepage
+// isn't found, return false.
+bool WideToCodepage(const std::wstring& wide,
+ const char* codepage_name,
+ OnStringUtilConversionError::Type on_error,
+ std::string* encoded) {
+ encoded->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ const UChar* uchar_src;
+ int uchar_len;
+#ifdef U_WCHAR_IS_UTF16
+ uchar_src = wide.c_str();
+ uchar_len = static_cast<int>(wide.length());
+#else // U_WCHAR_IS_UTF16
+ // When wchar_t is wider than UChar (16 bits), transform |wide| into a
+ // UChar* string. Size the UChar* buffer to be large enough to hold twice
+ // as many UTF-16 code points as there are UCS-4 characters, in case each
+ // character translates to a UTF-16 surrogate pair, and leave room for a NUL
+ // terminator.
+ std::vector<UChar> wide_uchar(wide.length() * 2 + 1);
+ u_strFromWCS(&wide_uchar[0], wide_uchar.size(), &uchar_len,
+ wide.c_str(), wide.length(), &status);
+ uchar_src = &wide_uchar[0];
+ DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*";
+#endif // U_WCHAR_IS_UTF16
+
+ int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len,
+ ucnv_getMaxCharSize(converter));
+ encoded->resize(encoded_max_length);
+
+ // Setup our error handler.
+ switch (on_error) {
+ case OnStringUtilConversionError::FAIL:
+ ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, 0,
+ NULL, NULL, &status);
+ break;
+ case OnStringUtilConversionError::SKIP:
+ ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, 0,
+ NULL, NULL, &status);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ // ucnv_fromUChars returns size not including terminating null
+ int actual_size = ucnv_fromUChars(converter, &(*encoded)[0],
+ encoded_max_length, uchar_src, uchar_len, &status);
+ encoded->resize(actual_size);
+ ucnv_close(converter);
+ if (U_SUCCESS(status))
+ return true;
+ encoded->clear(); // Make sure the output is empty on error.
+ return false;
+}
+
+// Converts a string of the given codepage into unicode.
+// If the codepage isn't found, return false.
+bool CodepageToWide(const std::string& encoded,
+ const char* codepage_name,
+ OnStringUtilConversionError::Type on_error,
+ std::wstring* wide) {
+ wide->clear();
+
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* converter = ucnv_open(codepage_name, &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ // The worst case is all the input characters are non-BMP (32-bit) ones.
+ size_t uchar_max_length = encoded.length() * 2 + 1;
+
+ UChar* uchar_dst;
+#ifdef U_WCHAR_IS_UTF16
+ uchar_dst = WriteInto(wide, uchar_max_length);
+#else
+ // When wchar_t is wider than UChar (16 bits), convert into a temporary
+ // UChar* buffer.
+ std::vector<UChar> wide_uchar(uchar_max_length);
+ uchar_dst = &wide_uchar[0];
+#endif // U_WCHAR_IS_UTF16
+
+ // Setup our error handler.
+ switch (on_error) {
+ case OnStringUtilConversionError::FAIL:
+ ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, 0,
+ NULL, NULL, &status);
+ break;
+ case OnStringUtilConversionError::SKIP:
+ ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, 0,
+ NULL, NULL, &status);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ int actual_size = ucnv_toUChars(converter,
+ uchar_dst,
+ static_cast<int>(uchar_max_length),
+ encoded.data(),
+ static_cast<int>(encoded.length()),
+ &status);
+ ucnv_close(converter);
+ if (!U_SUCCESS(status)) {
+ wide->clear(); // Make sure the output is empty on error.
+ return false;
+ }
+
+#ifndef U_WCHAR_IS_UTF16
+ // When wchar_t is wider than UChar (16 bits), it's not possible to wind up
+ // with any more wchar_t elements than UChar elements. ucnv_toUChars
+ // returns the number of UChar elements not including the NUL terminator, so
+ // leave extra room for that.
+ u_strToWCS(WriteInto(wide, actual_size + 1), actual_size + 1, &actual_size,
+ uchar_dst, actual_size, &status);
+ DCHECK(U_SUCCESS(status)) << "failed to convert UChar* to wstring";
+#endif // U_WCHAR_IS_UTF16
+
+ wide->resize(actual_size);
+ return true;
+}
+
+// Number formatting -----------------------------------------------------------
+
+// TODO: http://b/id=1092584 Come up with a portable pthread_once, and use
+// that to keep a singleton instead of putting it in the platform-dependent
+// file.
+NumberFormat* NumberFormatSingleton();
+
+std::wstring FormatNumber(int64 number) {
+ NumberFormat* number_format = NumberFormatSingleton();
+ if (!number_format) {
+ // As a fallback, just return the raw number in a string.
+ return StringPrintf(L"%lld", number);
+ }
+ UnicodeString ustr;
+ number_format->format(number, ustr);
+
+#ifdef U_WCHAR_IS_UTF16
+ return std::wstring(ustr.getBuffer(),
+ static_cast<std::wstring::size_type>(ustr.length()));
+#else // U_WCHAR_IS_UTF16
+ wchar_t buffer[64]; // A int64 is less than 20 chars long, so 64 chars
+ // leaves plenty of room for formating stuff.
+ int length = 0;
+ UErrorCode error = U_ZERO_ERROR;
+ u_strToWCS(buffer, 64, &length, ustr.getBuffer(), ustr.length() , &error);
+ if (U_FAILURE(error)) {
+ NOTREACHED();
+ // As a fallback, just return the raw number in a string.
+ return StringPrintf(L"%lld", number);
+ }
+ return std::wstring(buffer, static_cast<std::wstring::size_type>(length));
+#endif // U_WCHAR_IS_UTF16
+}
diff --git a/base/string_util_mac.cc b/base/string_util_mac.cc
new file mode 100644
index 0000000..4c5f3dc
--- /dev/null
+++ b/base/string_util_mac.cc
@@ -0,0 +1,239 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_util.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <pthread.h>
+#include <string>
+#include <vector>
+#include "base/logging.h"
+#include "base/scoped_cftyperef.h"
+#include "unicode/numfmt.h"
+
+// Can't use strlcpy/wcslcpy, because they always returns the length of src,
+// making it impossible to detect overflow. Because the reimplementation is
+// too large to inline, StrNCpy and WcsNCpy are in this file, but since they
+// don't make any non-inlined calls, there's no penalty relative to the libc
+// routines.
+template<typename CharType>
+static inline bool StrNCpyT(CharType* dst, const CharType* src,
+ size_t dst_size, size_t src_size) {
+ // The initial value of count has room for a NUL terminator.
+ size_t count = std::min(dst_size, src_size + 1);
+ if (count == 0)
+ return false;
+
+ // Copy up to (count - 1) bytes, or until reaching a NUL terminator
+ while (--count != 0) {
+ if ((*dst++ = *src++) == '\0')
+ break;
+ }
+
+ // If the break never occurred, append a NUL terminator
+ if (count == 0) {
+ *dst = '\0';
+
+ // If the string was truncated, return false
+ if (*src != '\0')
+ return false;
+ }
+
+ return true;
+}
+
+bool StrNCpy(char* dst, const char* src,
+ size_t dst_size, size_t src_size) {
+ return StrNCpyT(dst, src, dst_size, src_size);
+}
+
+bool WcsNCpy(wchar_t* dst, const wchar_t* src,
+ size_t dst_size, size_t src_size) {
+ return StrNCpyT(dst, src, dst_size, src_size);
+}
+
+static NumberFormat* number_format_singleton = NULL;
+static CFDateFormatterRef date_formatter = NULL;
+static CFDateFormatterRef time_formatter = NULL;
+
+static void DoInitializeStatics() {
+ UErrorCode status = U_ZERO_ERROR;
+ number_format_singleton = NumberFormat::createInstance(status);
+ DCHECK(U_SUCCESS(status));
+
+ scoped_cftyperef<CFLocaleRef> user_locale(CFLocaleCopyCurrent());
+ date_formatter = CFDateFormatterCreate(NULL,
+ user_locale,
+ kCFDateFormatterShortStyle, // date
+ NULL); // time
+ DCHECK(date_formatter);
+ time_formatter = CFDateFormatterCreate(NULL,
+ user_locale,
+ NULL, // date
+ kCFDateFormatterShortStyle); // time
+ DCHECK(time_formatter);
+}
+
+static void InitializeStatics() {
+ static pthread_once_t pthread_once_initialized = PTHREAD_ONCE_INIT;
+ pthread_once(&pthread_once_initialized, DoInitializeStatics);
+}
+
+// Convert the supplied cfsring into the specified encoding, and return it as
+// an STL string of the template type. Returns an empty string on failure.
+template<typename StringType>
+static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring,
+ CFStringEncoding encoding) {
+ CFIndex length = CFStringGetLength(cfstring);
+ if (length == 0)
+ return StringType();
+
+ CFRange whole_string = CFRangeMake(0, length);
+ CFIndex out_size;
+ CFIndex converted = CFStringGetBytes(cfstring,
+ whole_string,
+ encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ NULL, // buffer
+ 0, // maxBufLen
+ &out_size);
+ DCHECK(converted != 0 && out_size != 0);
+ if (converted == 0 || out_size == 0)
+ return StringType();
+
+ // out_size is the number of UInt8-sized units needed in the destination.
+ // A buffer allocated as UInt8 units might not be properly aligned to
+ // contain elements of StringType::value_type. Use a container for the
+ // proper value_type, and convert out_size by figuring the number of
+ // value_type elements per UInt8. Leave room for a NUL terminator.
+ typename StringType::size_type elements =
+ out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1;
+
+ // Make sure that integer truncation didn't occur. For the conversions done
+ // here, it never should.
+ DCHECK(((out_size * sizeof(UInt8)) %
+ sizeof(typename StringType::value_type)) == 0);
+
+ std::vector<typename StringType::value_type> out_buffer(elements);
+ converted = CFStringGetBytes(cfstring,
+ whole_string,
+ encoding,
+ 0, // lossByte
+ false, // isExternalRepresentation
+ reinterpret_cast<UInt8*>(&out_buffer[0]),
+ out_size,
+ NULL); // usedBufLen
+ DCHECK(converted != 0);
+ if (converted == 0)
+ return StringType();
+
+ out_buffer[elements - 1] = '\0';
+ return StringType(&out_buffer[0]);
+}
+
+// Given an STL string |in| with an encoding specified by |in_encoding|,
+// convert it to |out_encoding| and return it as an STL string of the
+// |OutStringType| template type. Returns an empty string on failure.
+template<typename OutStringType, typename InStringType>
+static OutStringType STLStringToSTLStringWithEncodingsT(
+ const InStringType& in,
+ CFStringEncoding in_encoding,
+ CFStringEncoding out_encoding) {
+ typename InStringType::size_type in_length = in.length();
+ if (in_length == 0)
+ return OutStringType();
+
+ scoped_cftyperef<CFStringRef> cfstring(
+ CFStringCreateWithBytesNoCopy(NULL,
+ reinterpret_cast<const UInt8*>(in.c_str()),
+ in_length *
+ sizeof(typename InStringType::value_type),
+ in_encoding,
+ false,
+ kCFAllocatorNull));
+ DCHECK(cfstring);
+ if (!cfstring)
+ return OutStringType();
+
+ return CFStringToSTLStringWithEncodingT<OutStringType>(cfstring,
+ out_encoding);
+}
+
+// Specify the byte ordering explicitly, otherwise CFString will be confused
+// when strings don't carry BOMs, as they typically won't.
+static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
+#ifdef __BIG_ENDIAN__
+#if defined(__WCHAR_MAX__) && __WCHAR_MAX__ == 0xffff
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF16BE;
+#else // __WCHAR_MAX__
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
+#endif // __WCHAR_MAX__
+#else // __BIG_ENDIAN__
+#if defined(__WCHAR_MAX__) && __WCHAR_MAX__ == 0xffff
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF16LE;
+#else // __WCHAR_MAX__
+static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
+#endif // __WCHAR_MAX__
+#endif // __BIG_ENDIAN__
+
+std::string WideToUTF8(const std::wstring& wide) {
+ return STLStringToSTLStringWithEncodingsT<std::string>(
+ wide, kWideStringEncoding, kNarrowStringEncoding);
+}
+
+std::wstring UTF8ToWide(const std::string& utf8) {
+ return STLStringToSTLStringWithEncodingsT<std::wstring>(
+ utf8, kNarrowStringEncoding, kWideStringEncoding);
+}
+
+// Technically, the native multibyte encoding would be the encoding returned
+// by CFStringGetSystemEncoding or GetApplicationTextEncoding, but I can't
+// imagine anyone needing or using that from these APIs, so just treat UTF-8
+// as though it were the native multibyte encoding.
+std::string WideToNativeMB(const std::wstring& wide) {
+ return WideToUTF8(wide);
+}
+
+std::wstring NativeMBToWide(const std::string& native_mb) {
+ return UTF8ToWide(native_mb);
+}
+
+NumberFormat* NumberFormatSingleton() {
+ InitializeStatics();
+ return number_format_singleton;
+}
+
+int64 StringToInt64(const std::string& value) {
+ return atoll(value.c_str());
+}
+
+int64 StringToInt64(const std::wstring& value) {
+ return wcstoll(value.c_str(), NULL, 10);
+}
diff --git a/base/string_util_mac.h b/base/string_util_mac.h
new file mode 100644
index 0000000..26201b7
--- /dev/null
+++ b/base/string_util_mac.h
@@ -0,0 +1,62 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STRING_UTIL_MAC_H__
+#define BASE_STRING_UTIL_MAC_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+inline bool StrCpy(char* dst, const char* src, size_t dst_size) {
+ return strlcpy(dst, src, dst_size) < dst_size;
+}
+
+inline int StrNCaseCmp(const char* string1, const char* string2, size_t count) {
+ return strncasecmp(string1, string2, count);
+}
+
+inline int VSNPrintF(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ return vsnprintf(buffer, size, format, arguments);
+}
+
+inline bool WcsCpy(char* dst, const char* src, size_t dst_size) {
+ return strlcpy(dst, src, dst_size) < dst_size;
+}
+
+inline int VSWPrintF(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ return vswprintf(buffer, size, format, arguments);
+}
+
+// StrNCpy and WcsNCpy are not inline, so they're implemented in the .cc file.
+
+#endif // BASE_STRING_UTIL_MAC_H__
diff --git a/base/string_util_unittest.cc b/base/string_util_unittest.cc
new file mode 100644
index 0000000..c6ff622
--- /dev/null
+++ b/base/string_util_unittest.cc
@@ -0,0 +1,848 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <sstream>
+#include <stdarg.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+}
+
+static const struct trim_case {
+ const wchar_t* input;
+ const TrimPositions positions;
+ const wchar_t* output;
+ const TrimPositions return_value;
+} trim_cases[] = {
+ {L" Google Video ", TRIM_LEADING, L"Google Video ", TRIM_LEADING},
+ {L" Google Video ", TRIM_TRAILING, L" Google Video", TRIM_TRAILING},
+ {L" Google Video ", TRIM_ALL, L"Google Video", TRIM_ALL},
+ {L"Google Video", TRIM_ALL, L"Google Video", TRIM_NONE},
+ {L"", TRIM_ALL, L"", TRIM_NONE},
+ {L" ", TRIM_LEADING, L"", TRIM_LEADING},
+ {L" ", TRIM_TRAILING, L"", TRIM_TRAILING},
+ {L" ", TRIM_ALL, L"", TRIM_ALL},
+ {L"\t\rTest String\n", TRIM_ALL, L"Test String", TRIM_ALL},
+ {L"\x2002Test String\x00A0\x3000", TRIM_ALL, L"Test String", TRIM_ALL},
+};
+
+static const struct trim_case_ascii {
+ const char* input;
+ const TrimPositions positions;
+ const char* output;
+ const TrimPositions return_value;
+} trim_cases_ascii[] = {
+ {" Google Video ", TRIM_LEADING, "Google Video ", TRIM_LEADING},
+ {" Google Video ", TRIM_TRAILING, " Google Video", TRIM_TRAILING},
+ {" Google Video ", TRIM_ALL, "Google Video", TRIM_ALL},
+ {"Google Video", TRIM_ALL, "Google Video", TRIM_NONE},
+ {"", TRIM_ALL, "", TRIM_NONE},
+ {" ", TRIM_LEADING, "", TRIM_LEADING},
+ {" ", TRIM_TRAILING, "", TRIM_TRAILING},
+ {" ", TRIM_ALL, "", TRIM_ALL},
+ {"\t\rTest String\n", TRIM_ALL, "Test String", TRIM_ALL},
+ {"\x85Test String\xa0\x20", TRIM_ALL, "Test String", TRIM_ALL},
+};
+
+TEST(StringUtilTest, TrimWhitespace) {
+ std::wstring output; // Allow contents to carry over to next testcase
+ for (int i = 0; i < arraysize(trim_cases); ++i) {
+ const trim_case& value = trim_cases[i];
+ EXPECT_EQ(value.return_value,
+ TrimWhitespace(value.input, value.positions, &output));
+ EXPECT_EQ(value.output, output);
+ }
+
+ // Test that TrimWhitespace() can take the same string for input and output
+ output = L" This is a test \r\n";
+ EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+ EXPECT_EQ(L"This is a test", output);
+
+ // Once more, but with a string of whitespace
+ output = L" \r\n";
+ EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output));
+ EXPECT_EQ(L"", output);
+
+ std::string output_ascii;
+ for (int i = 0; i < arraysize(trim_cases_ascii); ++i) {
+ const trim_case_ascii& value = trim_cases_ascii[i];
+ EXPECT_EQ(value.return_value,
+ TrimWhitespace(value.input, value.positions, &output_ascii));
+ EXPECT_EQ(value.output, output_ascii);
+ }
+}
+
+static const struct collapse_case {
+ const wchar_t* input;
+ const bool trim;
+ const wchar_t* output;
+} collapse_cases[] = {
+ {L" Google Video ", false, L"Google Video"},
+ {L"Google Video", false, L"Google Video"},
+ {L"", false, L""},
+ {L" ", false, L""},
+ {L"\t\rTest String\n", false, L"Test String"},
+ {L"\x2002Test String\x00A0\x3000", false, L"Test String"},
+ {L" Test \n \t String ", false, L"Test String"},
+ {L"\x2002Test\x1680 \x2028 \tString\x00A0\x3000", false, L"Test String"},
+ {L" Test String", false, L"Test String"},
+ {L"Test String ", false, L"Test String"},
+ {L"Test String", false, L"Test String"},
+ {L"", true, L""},
+ {L"\n", true, L""},
+ {L" \r ", true, L""},
+ {L"\nFoo", true, L"Foo"},
+ {L"\r Foo ", true, L"Foo"},
+ {L" Foo bar ", true, L"Foo bar"},
+ {L" \tFoo bar \n", true, L"Foo bar"},
+ {L" a \r b\n c \r\n d \t\re \t f \n ", true, L"abcde f"},
+};
+
+TEST(StringUtilTest, CollapseWhitespace) {
+ for (int i = 0; i < arraysize(collapse_cases); ++i) {
+ const collapse_case& value = collapse_cases[i];
+ EXPECT_EQ(value.output, CollapseWhitespace(value.input, value.trim));
+ }
+}
+
+static const wchar_t* const kConvertRoundtripCases[] = {
+ L"Google Video",
+ // "网页 图片 资讯更多 »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // "Παγκόσμιος Ιστός"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // "Поиск страниц на русском"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "전체서비스"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+
+ // Test a character that takes more than 16-bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+ #ifdef WIN32
+ L"\xd800\xdf00",
+ #else
+ "\x10300,
+ #endif
+};
+
+TEST(StringUtilTest, ConvertUTF8AndWide) {
+ // we round-trip all the wide strings through UTF-8 to make sure everything
+ // agrees on the conversion. This uses the stream operators to test them
+ // simultaneously.
+ for (int i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8;
+ utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+ std::wostringstream wide;
+ wide << UTF8ToWide(utf8.str());
+
+ EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+ }
+}
+
+TEST(StringUtilTest, ConvertUTF8AndWideEmptyString) {
+ // An empty std::wstring should be converted to an empty std::string,
+ // and vice versa.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToUTF8(wempty));
+ EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(StringUtilTest, ConvertMultiString) {
+ static wchar_t wmulti[] = {
+ L'f', L'o', L'o', L'\0',
+ L'b', L'a', L'r', L'\0',
+ L'b', L'a', L'z', L'\0',
+ L'\0'
+ };
+ static char multi[] = {
+ 'f', 'o', 'o', '\0',
+ 'b', 'a', 'r', '\0',
+ 'b', 'a', 'z', '\0',
+ '\0'
+ };
+ std::wstring wmultistring;
+ memcpy(WriteInto(&wmultistring, arraysize(wmulti)), wmulti, sizeof(wmulti));
+ EXPECT_EQ(arraysize(wmulti) - 1, wmultistring.length());
+ std::string expected;
+ memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+ EXPECT_EQ(arraysize(multi) - 1, expected.length());
+ const std::string& converted = WideToUTF8(wmultistring);
+ EXPECT_EQ(arraysize(multi) - 1, converted.length());
+ EXPECT_EQ(expected, converted);
+}
+
+TEST(StringUtilTest, ConvertCodepageUTF8) {
+ // Make sure WideToCodepage works like WideToUTF8.
+ for (int i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::string expected(WideToUTF8(kConvertRoundtripCases[i]));
+ std::string utf8;
+ EXPECT_TRUE(WideToCodepage(kConvertRoundtripCases[i], kCodepageUTF8,
+ OnStringUtilConversionError::SKIP, &utf8));
+ EXPECT_EQ(expected, utf8);
+ }
+}
+
+TEST(StringUtilTest, ConvertBetweenCodepageAndWide) {
+ static const struct {
+ const char* codepage_name;
+ const char* encoded;
+ OnStringUtilConversionError::Type on_error;
+ bool success;
+ const wchar_t* wide;
+ } kConvertCodepageCases[] = {
+ // Test a case where the input can no be decoded, using both SKIP and FAIL
+ // error handling rules. "A7 41" is valid, but "A6" isn't.
+ {"big5",
+ "\xA7\x41\xA6",
+ OnStringUtilConversionError::FAIL,
+ false,
+ L""},
+ {"big5",
+ "\xA7\x41\xA6",
+ OnStringUtilConversionError::SKIP,
+ true,
+ L"\x4F60"},
+ // Arabic (ISO-8859)
+ {"iso-8859-6",
+ "\xC7\xEE\xE4\xD3\xF1\xEE\xE4\xC7\xE5\xEF" " "
+ "\xD9\xEE\xE4\xEE\xEA\xF2\xE3\xEF\xE5\xF2",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x0627\x064E\x0644\x0633\x0651\x064E\x0644\x0627\x0645\x064F" L" "
+ L"\x0639\x064E\x0644\x064E\x064A\x0652\x0643\x064F\x0645\x0652"},
+ // Chinese Simplified (GB2312)
+ {"gb2312",
+ "\xC4\xE3\xBA\xC3",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x4F60\x597D"},
+ // Chinese Traditional (BIG5)
+ {"big5",
+ "\xA7\x41\xA6\x6E",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x4F60\x597D"},
+ // Greek (ISO-8859)
+ {"iso-8859-7",
+ "\xE3\xE5\xE9\xDC" " " "\xF3\xEF\xF5",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"},
+ // Hebrew (Windows)
+ {"windows-1255", /* to be replaced with "iso-8859-8-I"? */
+ "\xF9\xD1\xC8\xEC\xE5\xC9\xED",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD"},
+ // Hindi Devanagari (ISCII)
+ {"iscii-dev",
+ "\xEF\x42" "\xC6\xCC\xD7\xE8\xB3\xDA\xCF",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x0928\x092E\x0938\x094D\x0915\x093E\x0930"},
+ // Korean (EUC)
+ {"euc-kr",
+ "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\xC548\xB155\xD558\xC138\xC694"},
+ // Japanese (EUC)
+ {"euc-jp",
+ "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F"},
+ // Japanese (ISO-2022)
+ {"iso-2022-jp",
+ "\x1B\x24\x42" "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F" "\x1B\x28\x42",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F"},
+ // Japanese (Shift-JIS)
+ {"sjis",
+ "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x3053\x3093\x306B\x3061\x306F"},
+ // Russian (KOI8)
+ {"koi8-r",
+ "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432"
+ L"\x0443\x0439\x0442\x0435"},
+ // Thai (ISO-8859)
+ {"windows-874", /* to be replaced with "iso-8859-11". */
+ "\xCA\xC7\xD1\xCA\xB4\xD5" "\xA4\xC3\xD1\xBA",
+ OnStringUtilConversionError::FAIL,
+ true,
+ L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35"
+ L"\x0E04\x0E23\x0e31\x0E1A"},
+ };
+
+ for (int i = 0; i < arraysize(kConvertCodepageCases); ++i) {
+ std::wstring wide;
+ bool success = CodepageToWide(kConvertCodepageCases[i].encoded,
+ kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error,
+ &wide);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(kConvertCodepageCases[i].wide, wide);
+
+ // When decoding was successful and nothing was skipped, we also check the
+ // reverse conversion.
+ if (success &&
+ kConvertCodepageCases[i].on_error ==
+ OnStringUtilConversionError::FAIL) {
+ std::string encoded;
+ success = WideToCodepage(wide, kConvertCodepageCases[i].codepage_name,
+ kConvertCodepageCases[i].on_error, &encoded);
+ EXPECT_EQ(kConvertCodepageCases[i].success, success);
+ EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded);
+ }
+ }
+
+ // The above cases handled codepage->wide errors, but not wide->codepage.
+ // Test that here.
+ std::string encoded("Temp data"); // Make sure the string gets cleared.
+
+ // First test going to an encoding that can not represent that character.
+ EXPECT_FALSE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+ OnStringUtilConversionError::FAIL, &encoded));
+ EXPECT_TRUE(encoded.empty());
+ EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1",
+ OnStringUtilConversionError::SKIP, &encoded));
+ EXPECT_STREQ("Chinese", encoded.c_str());
+
+#ifdef WIN32
+ // When we're in UTF-16 mode, test an invalid UTF-16 character in the input.
+ EXPECT_FALSE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+ OnStringUtilConversionError::FAIL, &encoded));
+ EXPECT_TRUE(encoded.empty());
+ EXPECT_TRUE(WideToCodepage(L"a\xd800z", "iso-8859-1",
+ OnStringUtilConversionError::SKIP, &encoded));
+ EXPECT_STREQ("az", encoded.c_str());
+#endif
+
+ // Invalid characters should fail.
+ EXPECT_TRUE(WideToCodepage(L"a\xffffz", "iso-8859-1",
+ OnStringUtilConversionError::SKIP, &encoded));
+ EXPECT_STREQ("az", encoded.c_str());
+
+ // Invalid codepages should fail.
+ EXPECT_FALSE(WideToCodepage(L"Hello, world", "awesome-8571-2",
+ OnStringUtilConversionError::SKIP, &encoded));
+}
+
+TEST(StringUtilTest, ConvertASCII) {
+ static const char* char_cases[] = {
+ "Google Video",
+ "Hello, world\n",
+ "0123ABCDwxyz \a\b\t\r\n!+,.~"
+ };
+
+ static const wchar_t* const wchar_cases[] = {
+ L"Google Video",
+ L"Hello, world\n",
+ L"0123ABCDwxyz \a\b\t\r\n!+,.~"
+ };
+
+ for (int i = 0; i < arraysize(char_cases); ++i) {
+ EXPECT_TRUE(IsStringASCII(char_cases[i]));
+ std::wstring wide = ASCIIToWide(char_cases[i]);
+ EXPECT_EQ(wchar_cases[i], wide);
+
+ EXPECT_TRUE(IsStringASCII(wchar_cases[i]));
+ std::string ascii = WideToASCII(wchar_cases[i]);
+ EXPECT_EQ(char_cases[i], ascii);
+ }
+
+ EXPECT_FALSE(IsStringASCII("Google \x80Video"));
+ EXPECT_FALSE(IsStringASCII(L"Google \x80Video"));
+
+ // Convert empty strings.
+ std::wstring wempty;
+ std::string empty;
+ EXPECT_EQ(empty, WideToASCII(wempty));
+ EXPECT_EQ(wempty, ASCIIToWide(empty));
+}
+
+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"},
+};
+
+TEST(StringUtilTest, LowerCaseEqualsASCII) {
+ for (int i = 0; i < arraysize(lowercase_cases); ++i) {
+ EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_w,
+ lowercase_cases[i].dst));
+ EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_a,
+ lowercase_cases[i].dst));
+ }
+}
+
+TEST(StringUtilTest, GetByteDisplayUnits) {
+ static const struct {
+ int64 bytes;
+ DataUnits expected;
+ } cases[] = {
+ {0, DATA_UNITS_BYTE},
+ {512, DATA_UNITS_BYTE},
+ {10*1024, DATA_UNITS_KILOBYTE},
+ {10*1024*1024, DATA_UNITS_MEGABYTE},
+ {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE},
+ {~(1LL<<63), DATA_UNITS_GIGABYTE},
+#ifdef NDEBUG
+ {-1, DATA_UNITS_BYTE},
+#endif
+ };
+
+ for (int i = 0; i < arraysize(cases); ++i)
+ EXPECT_EQ(cases[i].expected, GetByteDisplayUnits(cases[i].bytes));
+}
+
+TEST(StringUtilTest, FormatBytes) {
+ static const struct {
+ int64 bytes;
+ DataUnits units;
+ const wchar_t* expected;
+ const wchar_t* expected_with_units;
+ } cases[] = {
+ {0, DATA_UNITS_BYTE, L"0", L"0 B"},
+ {512, DATA_UNITS_BYTE, L"512", L"512 B"},
+ {512, DATA_UNITS_KILOBYTE, L"0.5", L"0.5 kB"},
+ {1024*1024, DATA_UNITS_KILOBYTE, L"1024", L"1024 kB"},
+ {1024*1024, DATA_UNITS_MEGABYTE, L"1", L"1 MB"},
+ {1024*1024*1024, DATA_UNITS_GIGABYTE, L"1", L"1 GB"},
+ {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"},
+ {~(1LL<<63), DATA_UNITS_GIGABYTE, L"8589934592", L"8589934592 GB"},
+ // Make sure the first digit of the fractional part works.
+ {1024*1024 + 103, DATA_UNITS_KILOBYTE, L"1024.1", L"1024.1 kB"},
+ {1024*1024 + 205 * 1024, DATA_UNITS_MEGABYTE, L"1.2", L"1.2 MB"},
+ {1024*1024*1024 + (927 * 1024*1024), DATA_UNITS_GIGABYTE,
+ L"1.9", L"1.9 GB"},
+ {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"},
+#ifdef NDEBUG
+ {-1, DATA_UNITS_BYTE, L"", L""},
+#endif
+ };
+
+ for (int i = 0; i < arraysize(cases); ++i) {
+ EXPECT_EQ(cases[i].expected,
+ FormatBytes(cases[i].bytes, cases[i].units, false));
+ EXPECT_EQ(cases[i].expected_with_units,
+ FormatBytes(cases[i].bytes, cases[i].units, true));
+ }
+}
+
+TEST(StringUtilTest, ReplaceSubstringsAfterOffset) {
+ static const struct {
+ wchar_t* str;
+ std::wstring::size_type start_offset;
+ wchar_t* find_this;
+ wchar_t* replace_with;
+ wchar_t* expected;
+ } cases[] = {
+ {L"aaa", 0, L"a", L"b", L"bbb"},
+ {L"abb", 0, L"ab", L"a", L"ab"},
+ {L"Removing some substrings inging", 0, L"ing", L"", L"Remov some substrs "},
+ {L"Not found", 0, L"x", L"0", L"Not found"},
+ {L"Not found again", 5, L"x", L"0", L"Not found again"},
+ {L" Making it much longer ", 0, L" ", L"Four score and seven years ago",
+ L"Four score and seven years agoMakingFour score and seven years agoit"
+ L"Four score and seven years agomuchFour score and seven years agolonger"
+ L"Four score and seven years ago"},
+ {L"Invalid offset", 9999, L"t", L"foobar", L"Invalid offset"},
+ {L"Replace me only me once", 9, L"me ", L"", L"Replace me only once"},
+ {L"abababab", 2, L"ab", L"c", L"abccc"},
+ };
+
+ for (int i = 0; i < arraysize(cases); i++) {
+ std::wstring str(cases[i].str);
+ ReplaceSubstringsAfterOffset(&str, cases[i].start_offset,
+ cases[i].find_this, cases[i].replace_with);
+ EXPECT_EQ(cases[i].expected, str);
+ }
+}
+
+TEST(StringUtilTest, IntToString) {
+ static const struct {
+ int input;
+ std::string output;
+ } cases[] = {
+ {0, "0"},
+ {42, "42"},
+ {-42, "-42"},
+ {INT_MAX, "2147483647"},
+ {INT_MIN, "-2147483648"},
+ };
+
+ for (int i = 0; i < arraysize(cases); ++i)
+ EXPECT_EQ(cases[i].output, IntToString(cases[i].input));
+}
+
+TEST(StringUtilTest, Uint64ToString) {
+ static const struct {
+ uint64 input;
+ std::string output;
+ } cases[] = {
+ {0, "0"},
+ {42, "42"},
+ {INT_MAX, "2147483647"},
+ {kuint64max, "18446744073709551615"},
+ };
+
+ for (int i = 0; i < arraysize(cases); ++i)
+ EXPECT_EQ(cases[i].output, Uint64ToString(cases[i].input));
+}
+
+// This checks where we can use the assignment operator for a va_list. We need
+// a way to do this since Visual C doesn't support va_copy, but assignment on
+// va_list is not guaranteed to be a copy. See StringAppendVT which uses this
+// capability.
+static void VariableArgsFunc(const char* format, ...) {
+ va_list org;
+ va_start(org, format);
+
+ va_list dup = org;
+ int i1 = va_arg(org, int);
+ int j1 = va_arg(org, int);
+ char* s1 = va_arg(org, char*);
+ double d1 = va_arg(org, double);
+ va_end(org);
+
+ int i2 = va_arg(dup, int);
+ int j2 = va_arg(dup, int);
+ char* s2 = va_arg(dup, char*);
+ double d2 = va_arg(dup, double);
+
+ EXPECT_EQ(i1, i2);
+ EXPECT_EQ(j1, j2);
+ EXPECT_STREQ(s1, s2);
+ EXPECT_EQ(d1, d2);
+
+ va_end(dup);
+}
+
+TEST(StringUtilTest, VAList) {
+ VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21);
+}
+
+TEST(StringUtilTest, StringPrintfEmptyFormat) {
+ const char* empty = "";
+ EXPECT_EQ("", StringPrintf(empty));
+ EXPECT_EQ("", StringPrintf("%s", ""));
+}
+
+TEST(StringUtilTest, StringPrintfMisc) {
+ EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w'));
+ EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2s %1c", 123, L"hello", 'w'));
+}
+
+TEST(StringUtilTest, StringAppendfStringEmptyParam) {
+ std::string value("Hello");
+ StringAppendF(&value, "");
+ EXPECT_EQ("Hello", value);
+
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L"");
+ EXPECT_EQ(L"Hello", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfEmptyString) {
+ std::string value("Hello");
+ StringAppendF(&value, "%s", "");
+ EXPECT_EQ("Hello", value);
+
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L"%s", L"");
+ EXPECT_EQ(L"Hello", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfString) {
+ std::string value("Hello");
+ StringAppendF(&value, " %s", "World");
+ EXPECT_EQ("Hello World", value);
+
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L" %s", L"World");
+ EXPECT_EQ(L"Hello World", valuew);
+}
+
+TEST(StringUtilTest, StringAppendfInt) {
+ std::string value("Hello");
+ StringAppendF(&value, " %d", 123);
+ EXPECT_EQ("Hello 123", value);
+
+ std::wstring valuew(L"Hello");
+ StringAppendF(&valuew, L" %d", 123);
+ EXPECT_EQ(L"Hello 123", valuew);
+}
+
+// Make sure that lengths exactly around the initial buffer size are handled
+// correctly.
+TEST(StringUtilTest, StringPrintfBounds) {
+ const int src_len = 1026;
+ char src[src_len];
+ for (int i = 0; i < arraysize(src); i++)
+ src[i] = 'A';
+
+ wchar_t srcw[src_len];
+ for (int i = 0; i < arraysize(srcw); i++)
+ srcw[i] = 'A';
+
+ for (int i = 1; i < 3; i++) {
+ src[src_len - i] = 0;
+ std::string out;
+ SStringPrintf(&out, "%s", src);
+ EXPECT_STREQ(src, out.c_str());
+
+ srcw[src_len - i] = 0;
+ std::wstring outw;
+ SStringPrintf(&outw, L"%s", srcw);
+ EXPECT_STREQ(srcw, outw.c_str());
+ }
+}
+
+// Test very large sprintfs that will cause the buffer to grow.
+TEST(StringUtilTest, Grow) {
+ char src[1026];
+ for (int i = 0; i < arraysize(src); i++)
+ src[i] = 'A';
+ src[1025] = 0;
+
+ char* fmt = "%sB%sB%sB%sB%sB%sB%s";
+
+ std::string out;
+ SStringPrintf(&out, fmt, src, src, src, src, src, src, src);
+
+ char* ref = new char[320000];
+ sprintf_s(ref, 320000, fmt, src, src, src, src, src, src, src);
+
+ EXPECT_STREQ(ref, out.c_str());
+ delete ref;
+}
+
+// Test the boundary condition for the size of the string_util's
+// internal buffer.
+TEST(StringUtilTest, GrowBoundary) {
+ const int string_util_buf_len = 1024;
+ // Our buffer should be one larger than the size of StringAppendVT's stack
+ // buffer.
+ const int buf_len = string_util_buf_len + 1;
+ char src[buf_len + 1]; // Need extra one for NULL-terminator.
+ for (int i = 0; i < buf_len; ++i)
+ src[i] = 'a';
+ src[buf_len] = 0;
+
+ std::string out;
+ SStringPrintf(&out, "%s", src);
+
+ EXPECT_STREQ(src, out.c_str());
+}
+
+// sprintf in Visual Studio fails when given U+FFFF. This tests that the
+// failure case is gracefuly handled.
+TEST(StringUtilTest, Invalid) {
+ wchar_t invalid[2];
+ invalid[0] = 0xffff;
+ invalid[1] = 0;
+
+ std::wstring out;
+ SStringPrintf(&out, L"%s", invalid);
+ EXPECT_STREQ(L"", out.c_str());
+}
+
+// Test for SplitString
+TEST(StringUtilTest, SplitString) {
+ std::vector<std::wstring> r;
+
+ SplitString(L"a,b,c", L',', &r);
+ EXPECT_EQ(r.size(), 3);
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L"a, b, c", L',', &r);
+ EXPECT_EQ(r.size(), 3);
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L"a,,c", L',', &r);
+ EXPECT_EQ(r.size(), 3);
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"c");
+ r.clear();
+
+ SplitString(L"", L'*', &r);
+ EXPECT_EQ(r.size(), 1);
+ EXPECT_EQ(r[0], L"");
+ r.clear();
+
+ SplitString(L"foo", L'*', &r);
+ EXPECT_EQ(r.size(), 1);
+ EXPECT_EQ(r[0], L"foo");
+ r.clear();
+
+ SplitString(L"foo ,", L',', &r);
+ EXPECT_EQ(r.size(), 2);
+ EXPECT_EQ(r[0], L"foo");
+ EXPECT_EQ(r[1], L"");
+ r.clear();
+
+ SplitString(L",", L',', &r);
+ EXPECT_EQ(r.size(), 2);
+ EXPECT_EQ(r[0], L"");
+ EXPECT_EQ(r[1], L"");
+ r.clear();
+
+ SplitString(L"\t\ta\t", L'\t', &r);
+ EXPECT_EQ(r.size(), 4);
+ EXPECT_EQ(r[0], L"");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"a");
+ EXPECT_EQ(r[3], L"");
+ r.clear();
+
+ SplitStringDontTrim(L"\t\ta\t", L'\t', &r);
+ EXPECT_EQ(r.size(), 4);
+ EXPECT_EQ(r[0], L"");
+ EXPECT_EQ(r[1], L"");
+ EXPECT_EQ(r[2], L"a");
+ EXPECT_EQ(r[3], L"");
+ r.clear();
+
+ SplitString(L"\ta\t\nb\tcc", L'\n', &r);
+ EXPECT_EQ(r.size(), 2);
+ EXPECT_EQ(r[0], L"a");
+ EXPECT_EQ(r[1], L"b\tcc");
+ r.clear();
+
+ SplitStringDontTrim(L"\ta\t\nb\tcc", L'\n', &r);
+ EXPECT_EQ(r.size(), 2);
+ EXPECT_EQ(r[0], L"\ta\t");
+ EXPECT_EQ(r[1], L"b\tcc");
+ r.clear();
+}
+
+TEST(StringUtilTest, StartsWith) {
+ EXPECT_EQ(true, StartsWithASCII("javascript:url", "javascript", true));
+ EXPECT_EQ(true, StartsWithASCII("javascript:url", "javascript", false));
+ EXPECT_EQ(true, StartsWithASCII("JavaScript:url", "javascript", false));
+ EXPECT_EQ(false, StartsWithASCII("java", "javascript", true));
+ EXPECT_EQ(false, StartsWithASCII("java", "javascript", false));
+}
+
+TEST(StringUtilTest, GetStringFWithOffsets) {
+ std::vector<size_t> offsets;
+
+ ReplaceStringPlaceholders(L"Hello, $1. Your number is $2.", L"1", L"2",
+ &offsets);
+ EXPECT_EQ(2, offsets.size());
+ EXPECT_EQ(7, offsets[0]);
+ EXPECT_EQ(25, offsets[1]);
+ offsets.clear();
+
+ ReplaceStringPlaceholders(L"Hello, $2. Your number is $1.", L"1", L"2",
+ &offsets);
+ EXPECT_EQ(2, offsets.size());
+ EXPECT_EQ(25, offsets[0]);
+ EXPECT_EQ(7, offsets[1]);
+ offsets.clear();
+}
+
+TEST(StringUtilTest, SplitStringAlongWhitespace) {
+ struct TestData {
+ const std::wstring input;
+ const int expected_result_count;
+ const std::wstring output1;
+ const std::wstring output2;
+ } data[] = {
+ { L"a", 1, L"a", L"" },
+ { L" ", 0, L"", L"" },
+ { L" a", 1, L"a", L"" },
+ { L" ab ", 1, L"ab", L"" },
+ { L" ab c", 2, L"ab", L"c" },
+ { L" ab c ", 2, L"ab", L"c" },
+ { L" ab cd", 2, L"ab", L"cd" },
+ { L" ab cd ", 2, L"ab", L"cd" },
+ { L" \ta\t", 1, L"a", L"" },
+ { L" b\ta\t", 2, L"b", L"a" },
+ { L" b\tat", 2, L"b", L"at" },
+ { L"b\tat", 2, L"b", L"at" },
+ { L"b\t at", 2, L"b", L"at" },
+ };
+ for (size_t i = 0; i < arraysize(data); ++i) {
+ std::vector<std::wstring> results;
+ SplitStringAlongWhitespace(data[i].input, &results);
+ ASSERT_EQ(data[i].expected_result_count, results.size());
+ if (data[i].expected_result_count > 0)
+ ASSERT_EQ(data[i].output1, results[0]);
+ if (data[i].expected_result_count > 1)
+ ASSERT_EQ(data[i].output2, results[1]);
+ }
+}
+
+TEST(StringUtilTest, MatchPatternTest) {
+ EXPECT_EQ(MatchPattern(L"www.google.com", L"*.com"), true);
+ EXPECT_EQ(MatchPattern(L"www.google.com", L"*"), true);
+ EXPECT_EQ(MatchPattern(L"www.google.com", L"www*.g*.org"), false);
+ EXPECT_EQ(MatchPattern(L"Hello", L"H?l?o"), true);
+ EXPECT_EQ(MatchPattern(L"www.google.com", L"http://*)"), false);
+ EXPECT_EQ(MatchPattern(L"www.msn.com", L"*.COM"), false);
+ EXPECT_EQ(MatchPattern(L"Hello*1234", L"He??o\\*1*"), true);
+ EXPECT_EQ(MatchPattern(L"", L"*.*"), false);
+ EXPECT_EQ(MatchPattern(L"", L"*"), true);
+ EXPECT_EQ(MatchPattern(L"", L"?"), true);
+ EXPECT_EQ(MatchPattern(L"", L""), true);
+ EXPECT_EQ(MatchPattern(L"Hello", L""), false);
+ EXPECT_EQ(MatchPattern(L"Hello*", L"Hello*"), true);
+ EXPECT_EQ(MatchPattern("Hello*", "Hello*"), true); // narrow string
+}
+
+
diff --git a/base/string_util_win.cc b/base/string_util_win.cc
new file mode 100644
index 0000000..6cad854
--- /dev/null
+++ b/base/string_util_win.cc
@@ -0,0 +1,124 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/string_util.h"
+
+#include <windows.h>
+#include <string>
+#include "unicode/numfmt.h"
+#include "base/logging.h"
+
+// See WideToUTF8.
+static std::string WideToMultiByte(const std::wstring& wide, UINT code_page) {
+ int wide_length = static_cast<int>(wide.length());
+ if (wide_length == 0)
+ return std::string();
+
+ // compute the length of the buffer we'll need
+ int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
+ NULL, 0, NULL, NULL);
+ if (charcount == 0)
+ return std::string();
+
+ // convert
+ std::string mb;
+ WideCharToMultiByte(code_page, 0, wide.data(), wide_length,
+ WriteInto(&mb, charcount + 1), charcount, NULL, NULL);
+
+ return mb;
+}
+
+// Converts the given 8-bit string into a wide string, using the given
+// code page. The code page identifier is one accepted by MultiByteToWideChar()
+//
+// Danger: do not assert in this function, as it is used by the assertion code.
+// Doing so will cause an infinite loop.
+static std::wstring MultiByteToWide(const std::string& mb, UINT code_page) {
+ if (mb.length() == 0)
+ return std::wstring();
+
+ // compute the length of the buffer
+ int charcount = MultiByteToWideChar(code_page, 0, mb.c_str(), -1, NULL, 0);
+ if (charcount == 0)
+ return std::wstring();
+
+ // convert
+ std::wstring wide;
+ MultiByteToWideChar(code_page, 0, mb.c_str(), -1,
+ WriteInto(&wide, charcount), charcount);
+
+ return wide;
+}
+
+// Wide <--> UTF-8
+std::string WideToUTF8(const std::wstring& wide) {
+
+ return WideToMultiByte(wide, CP_UTF8);
+}
+
+std::wstring UTF8ToWide(const std::string& utf8) {
+ return MultiByteToWide(utf8, CP_UTF8);
+}
+
+// Wide <--> native multibyte
+std::string WideToNativeMB(const std::wstring& wide) {
+ return WideToMultiByte(wide, CP_ACP);
+}
+
+std::wstring NativeMBToWide(const std::string& native_mb) {
+ return MultiByteToWide(native_mb, CP_ACP);
+}
+
+NumberFormat* NumberFormatSingleton() {
+ static NumberFormat* number_format = NULL;
+ if (!number_format) {
+ // Make sure we are thread-safe.
+ UErrorCode status = U_ZERO_ERROR;
+ NumberFormat* new_number_format = NumberFormat::createInstance(status);
+ if (U_FAILURE(status)) {
+ NOTREACHED();
+ }
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID*>(&number_format), new_number_format, NULL)) {
+ // The old value was non-NULL, so no replacement was done. Another
+ // thread did the initialization out from under us.
+ if (new_number_format)
+ delete new_number_format;
+ }
+ }
+ return number_format;
+}
+
+int64 StringToInt64(const std::string& value) {
+ return _atoi64(value.c_str());
+}
+
+int64 StringToInt64(const std::wstring& value) {
+ return _wtoi64(value.c_str());
+}
diff --git a/base/string_util_win.h b/base/string_util_win.h
new file mode 100644
index 0000000..151a428
--- /dev/null
+++ b/base/string_util_win.h
@@ -0,0 +1,76 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_STRING_UTIL_WIN_H__
+#define BASE_STRING_UTIL_WIN_H__
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+
+inline bool StrCpy(char* dst, const char* src, size_t dst_size) {
+ return strcpy_s(dst, dst_size, src) == 0;
+}
+
+inline bool StrNCpy(char* dst, const char* src,
+ size_t dst_size, size_t src_size) {
+ return strncpy_s(dst, dst_size, src, src_size) == 0;
+}
+
+inline int StrNCaseCmp(const char* s1, const char* s2, size_t count) {
+ return _strnicmp(s1, s2, count);
+}
+
+inline int VSNPrintF(char* buffer, size_t size,
+ const char* format, va_list arguments) {
+ int length = vsnprintf_s(buffer, size, size - 1, format, arguments);
+ if (length < 0)
+ return _vscprintf(format, arguments);
+ return length;
+}
+
+inline bool WcsCpy(wchar_t* dst, const wchar_t* src, size_t dst_size) {
+ return wcscpy_s(dst, dst_size, src) == 0;
+}
+
+inline bool WcsNCpy(wchar_t* dst, const wchar_t* src,
+ size_t dst_size, size_t src_size) {
+ return wcsncpy_s(dst, dst_size, src, src_size) == 0;
+}
+
+inline int VSWPrintF(wchar_t* buffer, size_t size,
+ const wchar_t* format, va_list arguments) {
+ int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments);
+ if (length < 0)
+ return _vscwprintf(format, arguments);
+ return length;
+}
+
+#endif // BASE_STRING_UTIL_WIN_H__
diff --git a/base/task.h b/base/task.h
new file mode 100644
index 0000000..a964a17
--- /dev/null
+++ b/base/task.h
@@ -0,0 +1,725 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TASK_H__
+#define BASE_TASK_H__
+
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/non_thread_safe.h"
+#include "base/revocable_store.h"
+#include "base/tracked.h"
+#include "base/tuple.h"
+
+//------------------------------------------------------------------------------
+// Base class of Task, where we store info to help MessageLoop handle PostTask()
+// elements of Task processing.
+
+class Task;
+
+class MessageLoopOwnable : public tracked_objects::Tracked {
+ public:
+ MessageLoopOwnable() { Reset(); }
+ virtual ~MessageLoopOwnable() {}
+
+ // Use this method to adjust the priority given to a task by MessageLoop.
+ void set_priority(int priority) { priority_ = priority; }
+ int priority() const { return priority_; }
+
+ // Change whether this task will run in nested message loops.
+ void set_nestable(bool nestable) { nestable_ = nestable; }
+ bool nestable() { return nestable_; }
+
+
+ protected:
+ // If a derived class wishes to re-use this instance, then it should override
+ // this method. This method is called by MessageLoop after processing a task
+ // that was submitted to PostTask() or PostDelayedTask(). As seen, by default
+ // it deletes the task, but the derived class can change this behaviour and
+ // recycle (re-use) it. Be sure to call Reset() if you recycle it!
+ virtual void RecycleOrDelete() { delete this; }
+
+ // Call this method if you are trying to recycle a Task. Note that only
+ // derived classes should attempt this feat, as a replacement for creating a
+ // new instance.
+ void Reset() {
+ posted_task_delay_ = -1;
+ priority_ = 0;
+ next_task_ = NULL;
+ nestable_ = true;
+ }
+
+ private:
+ friend class TimerManager; // To check is_owned_by_message_loop().
+ friend class MessageLoop; // To maintain posted_task_delay().
+ friend class WorkerPool; // To release the task.
+
+ // Access methods used ONLY by friends in MessageLoop and TimerManager
+ int posted_task_delay() const { return posted_task_delay_; }
+ bool is_owned_by_message_loop() const { return 0 <= posted_task_delay_; }
+ void set_posted_task_delay(int delay) { posted_task_delay_ = delay; }
+
+ Task* next_task() const { return next_task_; }
+ void set_next_task(Task* next) { next_task_ = next; }
+
+ // Priority for execution by MessageLoop. 0 is default. Higher means run
+ // sooner, and lower (including negative) means run less soon.
+ int priority_;
+
+ // Slot to hold delay if the task was passed to PostTask(). If it was not
+ // passed to PostTask, then the delay is negative (the default).
+ int posted_task_delay_;
+
+ // When tasks are collected into a queue by MessageLoop, this member is used
+ // to form a null terminated list.
+ Task* next_task_;
+
+ // A nestable task will run in nested message loops, otherwise it will run
+ // only in the top level message loop.
+ bool nestable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MessageLoopOwnable);
+};
+
+
+// Task ------------------------------------------------------------------------
+//
+// A task is a generic runnable thingy, usually used for running code on a
+// different thread or for scheduling future tasks off of the message loop.
+
+class Task : public MessageLoopOwnable {
+ public:
+ Task() {}
+ virtual ~Task() {}
+
+ // Tasks are automatically deleted after Run is called.
+ virtual void Run() = 0;
+};
+
+class CancelableTask : public Task {
+ public:
+ // Not all tasks support cancellation.
+ virtual void Cancel() = 0;
+};
+
+// Scoped Factories ------------------------------------------------------------
+//
+// These scoped factory objects can be used by non-refcounted objects to safely
+// place tasks in a message loop. Each factory guarantees that the tasks it
+// produces will not run after the factory is destroyed. Commonly, factories
+// are declared as class members, so the class' tasks will automatically cancel
+// when the class instance is destroyed.
+//
+// Exampe Usage:
+//
+// class MyClass {
+// private:
+// // This factory will be used to schedule invocations of SomeMethod.
+// ScopedRunnableMethodFactory<MyClass> some_method_factory_;
+//
+// public:
+// // It is safe to suppress warning 4355 here.
+// MyClass() : some_method_factory_(this) { }
+//
+// void SomeMethod() {
+// // If this function might be called directly, you might want to revoke
+// // any outstanding runnable methods scheduled to call it. If it's not
+// // referenced other than by the factory, this is unnecessary.
+// some_method_factory_.RevokeAll();
+// ...
+// }
+//
+// void ScheduleSomeMethod() {
+// // If you'd like to only only have one pending task at a time, test for
+// // |empty| before manufacturing another task.
+// if (!some_method_factory_.empty())
+// return;
+//
+// // The factories are not thread safe, so always invoke on
+// // |MessageLoop::current()|.
+// MessageLoop::current()->PostTask(FROM_HERE,
+// some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod),
+// kSomeMethodDelayMS);
+// }
+// };
+
+// A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from
+// running after it is destroyed.
+template<class TaskType>
+class ScopedTaskFactory : public RevocableStore {
+ public:
+ ScopedTaskFactory() { }
+
+ // Create a new task.
+ inline TaskType* NewTask() {
+ return new TaskWrapper(this);
+ }
+
+ class TaskWrapper : public TaskType, public NonThreadSafe {
+ public:
+ explicit TaskWrapper(RevocableStore* store) : revocable_(store) { }
+
+ virtual void Run() {
+ if (!revocable_.revoked())
+ TaskType::Run();
+ }
+
+ private:
+ Revocable revocable_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper);
+ };
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory);
+};
+
+// A ScopedRunnableMethodFactory creates runnable methods for a specified
+// object. This is particularly useful for generating callbacks for
+// non-reference counted objects when the factory is a member of the object.
+template<class T>
+class ScopedRunnableMethodFactory : public RevocableStore {
+ public:
+ explicit ScopedRunnableMethodFactory(T* object) : object_(object) { }
+
+ template <class Method>
+ inline Task* NewRunnableMethod(Method method) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple0> >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple());
+ return task;
+ }
+
+ template <class Method, class A>
+ inline Task* NewRunnableMethod(Method method, const A& a) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple1<A> > >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple(a));
+ return task;
+ }
+
+ template <class Method, class A, class B>
+ inline Task* NewRunnableMethod(Method method, const A& a, const B& b) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple2<A, B> > >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple(a, b));
+ return task;
+ }
+
+ template <class Method, class A, class B, class C>
+ inline Task* NewRunnableMethod(Method method,
+ const A& a,
+ const B& b,
+ const C& c) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple3<A, B, C> > >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple(a, b, c));
+ return task;
+ }
+
+ template <class Method, class A, class B, class C, class D>
+ inline Task* NewRunnableMethod(Method method,
+ const A& a,
+ const B& b,
+ const C& c,
+ const D& d) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple4<A, B, C, D> > >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple(a, b, c, d));
+ return task;
+ }
+
+ template <class Method, class A, class B, class C, class D, class E>
+ inline Task* NewRunnableMethod(Method method,
+ const A& a,
+ const B& b,
+ const C& c,
+ const D& d,
+ const E& e) {
+ typedef typename ScopedTaskFactory<RunnableMethod<
+ Method, Tuple5<A, B, C, D, E> > >::TaskWrapper TaskWrapper;
+
+ TaskWrapper* task = new TaskWrapper(this);
+ task->Init(object_, method, MakeTuple(a, b, c, d, e));
+ return task;
+ }
+
+ protected:
+ template <class Method, class Params>
+ class RunnableMethod : public Task {
+ public:
+ RunnableMethod() { }
+
+ void Init(T* obj, Method meth, const Params& params) {
+ obj_ = obj;
+ meth_ = meth;
+ params_ = params;
+ }
+
+ virtual void Run() { DispatchToMethod(obj_, meth_, params_); }
+
+ private:
+ T* obj_;
+ Method meth_;
+ Params params_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod);
+ };
+
+ private:
+ T* object_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory);
+};
+
+// General task implementations ------------------------------------------------
+
+// Task to delete an object
+template<class T>
+class DeleteTask : public CancelableTask {
+ public:
+ explicit DeleteTask(T* obj) : obj_(obj) {
+ set_nestable(false);
+ }
+ virtual void Run() {
+ delete obj_;
+ }
+ virtual void Cancel() {
+ obj_ = NULL;
+ }
+ private:
+ T* obj_;
+};
+
+// Task to Release() an object
+template<class T>
+class ReleaseTask : public CancelableTask {
+ public:
+ explicit ReleaseTask(T* obj) : obj_(obj) {
+ set_nestable(false);
+ }
+ virtual void Run() {
+ if (obj_)
+ obj_->Release();
+ }
+ virtual void Cancel() {
+ obj_ = NULL;
+ }
+ private:
+ T* obj_;
+};
+
+// RunnableMethodTraits --------------------------------------------------------
+//
+// This traits-class is used by RunnableMethod to manage the lifetime of the
+// callee object. By default, it is assumed that the callee supports AddRef
+// and Release methods. A particular class can specialize this template to
+// define other lifetime management. For example, if the callee is known to
+// live longer than the RunnableMethod object, then a RunnableMethodTraits
+// struct could be defined with empty RetainCallee and ReleaseCallee methods.
+
+template <class T>
+struct RunnableMethodTraits {
+ static void RetainCallee(T* obj) {
+ obj->AddRef();
+ }
+ static void ReleaseCallee(T* obj) {
+ obj->Release();
+ }
+};
+
+// RunnableMethod and RunnableFunction -----------------------------------------
+//
+// Runnable methods are a type of task that call a function on an object when
+// they are run. We implement both an object and a set of NewRunnableMethod and
+// NewRunnableFunction functions for convenience. These functions are
+// overloaded and will infer the template types, simplifying calling code.
+//
+// The template definitions all use the following names:
+// T - the class type of the object you're supplying
+// this is not needed for the Static version of the call
+// Method/Function - the signature of a pointer to the method or function you
+// want to call
+// Param - the parameter(s) to the method, possibly packed as a Tuple
+// A - the first parameter (if any) to the method
+// B - the second parameter (if any) to the mathod
+//
+// Put these all together and you get an object that can call a method whose
+// signature is:
+// R T::MyFunction([A[, B]])
+//
+// Usage:
+// PostTask(FROM_HERE, NewRunnableMethod(object, &Object::method[, a[, b]])
+// PostTask(FROM_HERE, NewRunnableFunction(&function[, a[, b]])
+
+// RunnableMethod and NewRunnableMethod implementation -------------------------
+
+template <class T, class Method, class Params>
+class RunnableMethod : public CancelableTask,
+ public RunnableMethodTraits<T> {
+ public:
+ RunnableMethod(T* obj, Method meth, const Params& params)
+ : obj_(obj), meth_(meth), params_(params) {
+ RetainCallee(obj_);
+ }
+ ~RunnableMethod() {
+ ReleaseCallee();
+ }
+
+ virtual void Run() {
+ if (obj_)
+ DispatchToMethod(obj_, meth_, params_);
+ }
+
+ virtual void Cancel() {
+ ReleaseCallee();
+ }
+
+ private:
+ void ReleaseCallee() {
+ if (obj_) {
+ RunnableMethodTraits<T>::ReleaseCallee(obj_);
+ obj_ = NULL;
+ }
+ }
+
+ T* obj_;
+ Method meth_;
+ Params params_;
+};
+
+template <class T, class Method>
+inline CancelableTask* NewRunnableMethod(T* object, Method method) {
+ return new RunnableMethod<T, Method, Tuple0>(object, method, MakeTuple());
+}
+
+template <class T, class Method, class A>
+inline CancelableTask* NewRunnableMethod(T* object, Method method, const A& a) {
+ return new RunnableMethod<T, Method, Tuple1<A> >(object, method, MakeTuple(a));
+}
+
+template <class T, class Method, class A, class B>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+const A& a, const B& b) {
+ return new RunnableMethod<T, Method, Tuple2<A, B> >(object, method,
+ MakeTuple(a, b));
+}
+
+template <class T, class Method, class A, class B, class C>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+ const A& a, const B& b, const C& c) {
+ return new RunnableMethod<T, Method, Tuple3<A, B, C> >(object, method,
+ MakeTuple(a, b, c));
+}
+
+template <class T, class Method, class A, class B, class C, class D>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+ const A& a, const B& b,
+ const C& c, const D& d) {
+ return new RunnableMethod<T, Method, Tuple4<A, B, C, D> >(object, method,
+ MakeTuple(a, b,
+ c, d));
+}
+
+template <class T, class Method, class A, class B, class C, class D, class E>
+inline CancelableTask* NewRunnableMethod(T* object, Method method,
+ const A& a, const B& b,
+ const C& c, const D& d, const E& e) {
+ return new RunnableMethod<T,
+ Method,
+ Tuple5<A, B, C, D, E> >(object,
+ method,
+ MakeTuple(a, b, c, d, e));
+}
+
+// RunnableFunction and NewRunnableFunction implementation ---------------------
+
+template <class Function, class Params>
+class RunnableFunction : public CancelableTask {
+ public:
+ RunnableFunction(Function function, const Params& params)
+ : function_(function), params_(params) {
+ }
+
+ ~RunnableFunction() {
+ }
+
+ virtual void Run() {
+ if (function_)
+ DispatchToFunction(function_, params_);
+ }
+
+ virtual void Cancel() {
+ }
+
+ private:
+ Function function_;
+ Params params_;
+};
+
+template <class Function>
+inline CancelableTask* NewRunnableFunction(Function function) {
+ return new RunnableFunction<Function, Tuple0>(function, MakeTuple());
+}
+
+template <class Function, class A>
+inline CancelableTask* NewRunnableFunction(Function function, const A& a) {
+ return new RunnableFunction<Function, Tuple1<A> >(function, MakeTuple(a));
+}
+
+template <class Function, class A, class B>
+inline CancelableTask* NewRunnableFunction(Function function,
+ const A& a, const B& b) {
+ return new RunnableFunction<Function, Tuple2<A, B> >(function, MakeTuple(a, b));
+}
+
+template <class Function, class A, class B, class C>
+inline CancelableTask* NewRunnableFunction(Function function,
+ const A& a, const B& b,
+ const C& c) {
+ return new RunnableFunction<Function, Tuple3<A, B, C> >(function,
+ MakeTuple(a, b, c));
+}
+
+template <class Function, class A, class B, class C, class D>
+inline CancelableTask* NewRunnableFunction(Function function,
+ const A& a, const B& b,
+ const C& c, const D& d) {
+ return new RunnableFunction<Function, Tuple4<A, B, C, D> >(function,
+ MakeTuple(a, b,
+ c, d));
+}
+
+template <class Function, class A, class B, class C, class D, class E>
+inline CancelableTask* NewRunnableFunction(Function function,
+ const A& a, const B& b,
+ const C& c, const D& d,
+ const E& e) {
+ return new RunnableFunction<Function, Tuple5<A, B, C, D, E> >(function,
+ MakeTuple(a, b,
+ c, d,
+ e));
+}
+
+// Callback --------------------------------------------------------------------
+//
+// A Callback is like a Task but with unbound parameters. It is basically an
+// object-oriented function pointer.
+//
+// Callbacks are designed to work with Tuples. A set of helper functions and
+// classes is provided to hide the Tuple details from the consumer. Client
+// code will generally work with the CallbackRunner base class, which merely
+// provides a Run method and is returned by the New* functions. This allows
+// users to not care which type of class implements the callback, only that it
+// has a certain number and type of arguments.
+//
+// The implementation of this is done by CallbackImpl, which inherits
+// CallbackStorage to store the data. This allows the storage of the data
+// (requiring the class type T) to be hidden from users, who will want to call
+// this regardless of the implementor's type T.
+//
+// Note that callbacks currently have no facility for cancelling or abandoning
+// them. We currently handle this at a higher level for cases where this is
+// necessary. The pointer in a callback must remain valid until the callback
+// is made.
+//
+// Like Task, the callback executor is responsible for deleting the callback
+// pointer once the callback has executed.
+//
+// Example client usage:
+// void Object::DoStuff(int, string);
+// Callback2<int, string>::Type* callback =
+// NewCallback(obj, &Object::DoStuff);
+// callback->Run(5, string("hello"));
+// delete callback;
+// or, equivalently, using tuples directly:
+// CallbackRunner<Tuple2<int, string> >* callback =
+// NewCallback(obj, &Object::DoStuff);
+// callback->RunWithParams(MakeTuple(5, string("hello")));
+
+// Base for all Callbacks that handles storage of the pointers.
+template <class T, typename Method>
+class CallbackStorage {
+ public:
+ CallbackStorage(T* obj, Method meth) : obj_(obj), meth_(meth) {
+ }
+
+ protected:
+ T* obj_;
+ Method meth_;
+};
+
+// Interface that is exposed to the consumer, that does the actual calling
+// of the method.
+template <typename Params>
+class CallbackRunner {
+ public:
+ typedef Params TupleType;
+
+ virtual ~CallbackRunner() {}
+ virtual void RunWithParams(const Params& params) = 0;
+
+ // Convenience functions so callers don't have to deal with Tuples.
+ inline void Run() {
+ RunWithParams(Tuple0());
+ }
+
+ template <typename Arg1>
+ inline void Run(const Arg1& a) {
+ RunWithParams(Params(a));
+ }
+
+ template <typename Arg1, typename Arg2>
+ inline void Run(const Arg1& a, const Arg2& b) {
+ RunWithParams(Params(a, b));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3>
+ inline void Run(const Arg1& a, const Arg2& b, const Arg3& c) {
+ RunWithParams(Params(a, b, c));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, const Arg4& d) {
+ RunWithParams(Params(a, b, c, d));
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+ inline void Run(const Arg1& a, const Arg2& b, const Arg3& c,
+ const Arg4& d, const Arg5& e) {
+ RunWithParams(Params(a, b, c, d, e));
+ }
+};
+
+template <class T, typename Method, typename Params>
+class CallbackImpl : public CallbackStorage<T, Method>,
+ public CallbackRunner<Params> {
+ public:
+ CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) {
+ }
+ virtual void RunWithParams(const Params& params) {
+ // use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ DispatchToMethod(this->obj_, this->meth_, params);
+ }
+};
+
+// 0-arg implementation
+struct Callback0 {
+ typedef CallbackRunner<Tuple0> Type;
+};
+
+template <class T>
+typename Callback0::Type* NewCallback(T* object, void (T::*method)()) {
+ return new CallbackImpl<T, void (T::*)(), Tuple0 >(object, method);
+}
+
+// 1-arg implementation
+template <typename Arg1>
+struct Callback1 {
+ typedef CallbackRunner<Tuple1<Arg1> > Type;
+};
+
+template <class T, typename Arg1>
+typename Callback1<Arg1>::Type* NewCallback(T* object, void (T::*method)(Arg1)) {
+ return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method);
+}
+
+// 2-arg implementation
+template <typename Arg1, typename Arg2>
+struct Callback2 {
+ typedef CallbackRunner<Tuple2<Arg1, Arg2> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2>
+typename Callback2<Arg1, Arg2>::Type* NewCallback(
+ T* object,
+ void (T::*method)(Arg1, Arg2)) {
+ return new CallbackImpl<T, void (T::*)(Arg1, Arg2),
+ Tuple2<Arg1, Arg2> >(object, method);
+}
+
+// 3-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3>
+struct Callback3 {
+ typedef CallbackRunner<Tuple3<Arg1, Arg2, Arg3> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2, typename Arg3>
+typename Callback3<Arg1, Arg2, Arg3>::Type* NewCallback(
+ T* object,
+ void (T::*method)(Arg1, Arg2, Arg3)) {
+ return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3),
+ Tuple3<Arg1, Arg2, Arg3> >(object, method);
+}
+
+// 4-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+struct Callback4 {
+ typedef CallbackRunner<Tuple4<Arg1, Arg2, Arg3, Arg4> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+typename Callback4<Arg1, Arg2, Arg3, Arg4>::Type* NewCallback(
+ T* object,
+ void (T::*method)(Arg1, Arg2, Arg3, Arg4)) {
+ return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4),
+ Tuple4<Arg1, Arg2, Arg3, Arg4> >(object, method);
+}
+
+// 5-arg implementation
+template <typename Arg1, typename Arg2, typename Arg3,
+ typename Arg4, typename Arg5>
+struct Callback5 {
+ typedef CallbackRunner<Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> > Type;
+};
+
+template <class T, typename Arg1, typename Arg2,
+ typename Arg3, typename Arg4, typename Arg5>
+typename Callback5<Arg1, Arg2, Arg3, Arg4, Arg5>::Type* NewCallback(
+ T* object,
+ void (T::*method)(Arg1, Arg2, Arg3, Arg4, Arg5)) {
+ return new CallbackImpl<T, void (T::*)(Arg1, Arg2, Arg3, Arg4, Arg5),
+ Tuple5<Arg1, Arg2, Arg3, Arg4, Arg5> >(object, method);
+}
+
+#endif // BASE_TASK_H__
diff --git a/base/test_suite.h b/base/test_suite.h
new file mode 100644
index 0000000..a021339
--- /dev/null
+++ b/base/test_suite.h
@@ -0,0 +1,115 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TEST_SUITE_H__
+#define BASE_TEST_SUITE_H__
+
+// Defines a basic test suite framework for running gtest based tests. You can
+// instantiate this class in your main function and call its Run method to run
+// any gtest based tests that are linked into your executable.
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/debug_on_start.h"
+#include "base/icu_util.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/multiprocess_test.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestSuite {
+ public:
+ TestSuite(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ }
+
+ virtual ~TestSuite() {
+ // Flush any remaining messages. This ensures that any accumulated Task
+ // objects get destroyed before we exit, which avoids noise in purify
+ // leak-test results.
+ message_loop_.Quit();
+ message_loop_.Run();
+ }
+
+ int Run() {
+ Initialize();
+
+ // Check to see if we are being run as a client process.
+ std::wstring client_func =
+ parsed_command_line_.GetSwitchValue(kRunClientProcess);
+ if (!client_func.empty()) {
+ // Convert our function name to a usable string for GetProcAddress.
+ std::string func_name(client_func.begin(), client_func.end());
+
+ // Get our module handle and search for an exported function
+ // which we can use as our client main.
+ MultiProcessTest::ChildFunctionPtr func =
+ reinterpret_cast<MultiProcessTest::ChildFunctionPtr>(
+ GetProcAddress(GetModuleHandle(NULL), func_name.c_str()));
+ if (func)
+ return func();
+ return -1;
+ }
+ return RUN_ALL_TESTS();
+ }
+
+ protected:
+ // All fatal log messages (e.g. DCHECK failures) imply unit test failures
+ static void UnitTestAssertHandler(const std::string& str) {
+ FAIL() << str;
+ }
+
+ // Disable crash dialogs so that it doesn't gum up the buildbot
+ virtual void SuppressErrorDialogs() {
+ UINT new_flags = SEM_FAILCRITICALERRORS |
+ SEM_NOGPFAULTERRORBOX |
+ SEM_NOOPENFILEERRORBOX;
+
+ // Preserve existing error mode, as discussed at http://t/dmea
+ UINT existing_flags = SetErrorMode(new_flags);
+ SetErrorMode(existing_flags | new_flags);
+ }
+
+ virtual void Initialize() {
+ // In some cases, we do not want to see standard error dialogs.
+ if (!IsDebuggerPresent() &&
+ !parsed_command_line_.HasSwitch(L"show-error-dialogs")) {
+ SuppressErrorDialogs();
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+ }
+
+ icu_util::Initialize();
+ }
+
+ CommandLine parsed_command_line_;
+ MessageLoop message_loop_;
+};
+
+#endif // BASE_TEST_SUITE_H__
diff --git a/base/third_party/nspr/README.google b/base/third_party/nspr/README.google
new file mode 100644
index 0000000..7775d60
--- /dev/null
+++ b/base/third_party/nspr/README.google
@@ -0,0 +1,2 @@
+The original code is the Netscape Portable Runtime (NSPR), licensed under
+the MPL/GPL/LGPL tri-license (http://www.mozilla.org/MPL/).
diff --git a/base/third_party/nspr/prcpucfg.h b/base/third_party/nspr/prcpucfg.h
new file mode 100644
index 0000000..ad1aec4
--- /dev/null
+++ b/base/third_party/nspr/prcpucfg.h
@@ -0,0 +1,41 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
+
+#if defined(WIN32)
+#include "base/third_party/nspr/prcpucfg_win.h"
+#elif defined(__APPLE__)
+#include "base/third_party/nspr/prcpucfg_mac.h"
+#else
+#error Provide a prcpucfg.h appropriate for your platform
+#endif
+
+#endif // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__
diff --git a/base/third_party/nspr/prcpucfg_mac.h b/base/third_party/nspr/prcpucfg_mac.h
new file mode 100644
index 0000000..dc7e0e0
--- /dev/null
+++ b/base/third_party/nspr/prcpucfg_mac.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_UNIX
+#define XP_UNIX
+#endif
+
+#define PR_AF_INET6 30 /* same as AF_INET6 */
+
+#if defined(i386)
+#undef IS_BIG_ENDIAN
+#define IS_LITTLE_ENDIAN 1
+#else
+#undef IS_LITTLE_ENDIAN
+#define IS_BIG_ENDIAN 1
+#endif
+
+#define HAVE_LONG_LONG
+#undef HAVE_ALIGNED_DOUBLES
+#define HAVE_ALIGNED_LONGLONGS 1
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 4
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+#define PR_ALIGN_OF_WORD 4
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
+
diff --git a/base/third_party/nspr/prcpucfg_win.h b/base/third_party/nspr/prcpucfg_win.h
new file mode 100644
index 0000000..026258b
--- /dev/null
+++ b/base/third_party/nspr/prcpucfg_win.h
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nspr_cpucfg___
+#define nspr_cpucfg___
+
+#ifndef XP_PC
+#define XP_PC
+#endif
+
+#ifndef WIN32
+#define WIN32
+#endif
+
+#ifndef WIN95
+#define WIN95
+#endif
+
+#define PR_AF_INET6 23 /* same as AF_INET6 */
+
+#if defined(_M_IX86) || defined(_X86_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 32
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 5
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 4
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 4
+#define PR_ALIGN_OF_POINTER 4
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 2
+
+#elif defined(_ALPHA_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_DOUBLE 8
+#define PR_BYTES_PER_WORD 4
+#define PR_BYTES_PER_DWORD 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_DOUBLE 64
+#define PR_BITS_PER_WORD 32
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_DOUBLE_LOG2 6
+#define PR_BITS_PER_WORD_LOG2 5
+
+#define PR_BYTES_PER_WORD_LOG2 2
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 4
+
+#elif defined(_AMD64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#elif defined(_IA64_)
+
+#define IS_LITTLE_ENDIAN 1
+#undef IS_BIG_ENDIAN
+#define IS_64
+
+#define PR_BYTES_PER_BYTE 1
+#define PR_BYTES_PER_SHORT 2
+#define PR_BYTES_PER_INT 4
+#define PR_BYTES_PER_INT64 8
+#define PR_BYTES_PER_LONG 4
+#define PR_BYTES_PER_FLOAT 4
+#define PR_BYTES_PER_WORD 8
+#define PR_BYTES_PER_DWORD 8
+#define PR_BYTES_PER_DOUBLE 8
+
+#define PR_BITS_PER_BYTE 8
+#define PR_BITS_PER_SHORT 16
+#define PR_BITS_PER_INT 32
+#define PR_BITS_PER_INT64 64
+#define PR_BITS_PER_LONG 32
+#define PR_BITS_PER_FLOAT 32
+#define PR_BITS_PER_WORD 64
+#define PR_BITS_PER_DWORD 64
+#define PR_BITS_PER_DOUBLE 64
+
+#define PR_BITS_PER_BYTE_LOG2 3
+#define PR_BITS_PER_SHORT_LOG2 4
+#define PR_BITS_PER_INT_LOG2 5
+#define PR_BITS_PER_INT64_LOG2 6
+#define PR_BITS_PER_LONG_LOG2 5
+#define PR_BITS_PER_FLOAT_LOG2 5
+#define PR_BITS_PER_WORD_LOG2 6
+#define PR_BITS_PER_DWORD_LOG2 6
+#define PR_BITS_PER_DOUBLE_LOG2 6
+
+#define PR_ALIGN_OF_SHORT 2
+#define PR_ALIGN_OF_INT 4
+#define PR_ALIGN_OF_LONG 4
+#define PR_ALIGN_OF_INT64 8
+#define PR_ALIGN_OF_FLOAT 4
+#define PR_ALIGN_OF_WORD 8
+#define PR_ALIGN_OF_DWORD 8
+#define PR_ALIGN_OF_DOUBLE 8
+#define PR_ALIGN_OF_POINTER 8
+
+#define PR_BYTES_PER_WORD_LOG2 3
+#define PR_BYTES_PER_DWORD_LOG2 3
+
+#else /* defined(_M_IX86) || defined(_X86_) */
+
+#error unknown processor architecture
+
+#endif /* defined(_M_IX86) || defined(_X86_) */
+
+#ifndef HAVE_LONG_LONG
+#define HAVE_LONG_LONG
+#endif
+
+#ifndef NO_NSPR_10_SUPPORT
+
+#define BYTES_PER_BYTE PR_BYTES_PER_BYTE
+#define BYTES_PER_SHORT PR_BYTES_PER_SHORT
+#define BYTES_PER_INT PR_BYTES_PER_INT
+#define BYTES_PER_INT64 PR_BYTES_PER_INT64
+#define BYTES_PER_LONG PR_BYTES_PER_LONG
+#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT
+#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE
+#define BYTES_PER_WORD PR_BYTES_PER_WORD
+#define BYTES_PER_DWORD PR_BYTES_PER_DWORD
+
+#define BITS_PER_BYTE PR_BITS_PER_BYTE
+#define BITS_PER_SHORT PR_BITS_PER_SHORT
+#define BITS_PER_INT PR_BITS_PER_INT
+#define BITS_PER_INT64 PR_BITS_PER_INT64
+#define BITS_PER_LONG PR_BITS_PER_LONG
+#define BITS_PER_FLOAT PR_BITS_PER_FLOAT
+#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE
+#define BITS_PER_WORD PR_BITS_PER_WORD
+
+#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2
+#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2
+#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2
+#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2
+#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2
+#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2
+#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2
+#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2
+
+#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT
+#define ALIGN_OF_INT PR_ALIGN_OF_INT
+#define ALIGN_OF_LONG PR_ALIGN_OF_LONG
+#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64
+#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT
+#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE
+#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER
+#define ALIGN_OF_WORD PR_ALIGN_OF_WORD
+
+#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2
+#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2
+#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2
+
+#endif /* NO_NSPR_10_SUPPORT */
+
+#endif /* nspr_cpucfg___ */
diff --git a/base/third_party/nspr/prtime.cc b/base/third_party/nspr/prtime.cc
new file mode 100644
index 0000000..a5e851c
--- /dev/null
+++ b/base/third_party/nspr/prtime.cc
@@ -0,0 +1,838 @@
+/*
+* Portions are Copyright (C) 2007 Google Inc
+*
+* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is the Netscape Portable Runtime (NSPR).
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998-2000
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK *****
+*
+*/
+
+/*
+ * prtime.cc --
+ * NOTE: The original nspr file name is prtime.c
+ *
+ * NSPR date and time functions
+ *
+ * CVS revision 3.31
+ */
+
+/*
+* The following functions were copied from the NSPR prtime.c file.
+* PR_ParseTimeString
+* PR_ImplodeTime
+* This was modified to use the Win32 SYSTEMTIME/FILETIME structures
+* and the timezone offsets are applied to the FILETIME structure.
+* All types and defines have been defined in the base/third_party/prtime.h file
+* These have been copied from the following nspr files. We have only copied over
+* the types we need.
+* 1. prtime.h
+* 2. prtypes.h
+* 3. prlong.h
+*/
+
+#include <windows.h>
+#include <time.h>
+#include "base/third_party/nspr/prtime.h"
+
+/*
+ *------------------------------------------------------------------------
+ *
+ * PR_ImplodeTime --
+ *
+ * Cf. time_t mktime(struct tm *tp)
+ * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough.
+ *
+ *------------------------------------------------------------------------
+ */
+PRTime
+PR_ImplodeTime(const PRExplodedTime *exploded)
+{
+ // Create the system struct representing our exploded time.
+ SYSTEMTIME st = {0};
+ FILETIME ft = {0};
+ ULARGE_INTEGER uli = {0};
+
+ st.wYear = exploded->tm_year;
+ st.wMonth = exploded->tm_month + 1;
+ st.wDayOfWeek = exploded->tm_wday;
+ st.wDay = exploded->tm_mday;
+ st.wHour = exploded->tm_hour;
+ st.wMinute = exploded->tm_min;
+ st.wSecond = exploded->tm_sec;
+ st.wMilliseconds = exploded->tm_usec/1000;
+ // Convert to FILETIME.
+ if (!SystemTimeToFileTime(&st, &ft)) {
+ NOTREACHED() << "Unable to convert time";
+ return 0;
+ }
+ // Apply offsets.
+ uli.LowPart = ft.dwLowDateTime;
+ uli.HighPart = ft.dwHighDateTime;
+ // From second to 100-ns
+ uli.QuadPart -=
+ (exploded->tm_params.tp_gmt_offset +
+ exploded->tm_params.tp_dst_offset) * 10000000i64; // 7 zeros
+ // Convert to PRTime
+ uli.QuadPart -= 116444736000000000i64; // from Windows epoch to NSPR epoch
+ uli.QuadPart /= 10; // from 100-nanosecond to microsecond
+ return (PRTime)uli.QuadPart;
+}
+
+/*
+ * The following code implements PR_ParseTimeString(). It is based on
+ * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski <jwz@netscape.com>.
+ */
+
+/*
+ * We only recognize the abbreviations of a small subset of time zones
+ * in North America, Europe, and Japan.
+ *
+ * PST/PDT: Pacific Standard/Daylight Time
+ * MST/MDT: Mountain Standard/Daylight Time
+ * CST/CDT: Central Standard/Daylight Time
+ * EST/EDT: Eastern Standard/Daylight Time
+ * AST: Atlantic Standard Time
+ * NST: Newfoundland Standard Time
+ * GMT: Greenwich Mean Time
+ * BST: British Summer Time
+ * MET: Middle Europe Time
+ * EET: Eastern Europe Time
+ * JST: Japan Standard Time
+ */
+
+typedef enum
+{
+ TT_UNKNOWN,
+
+ TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT,
+
+ TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN,
+ TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC,
+
+ TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT,
+ TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST
+} TIME_TOKEN;
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+PRStatus
+PR_ParseTimeString(
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result)
+{
+ PRExplodedTime tm;
+ TIME_TOKEN dotw = TT_UNKNOWN;
+ TIME_TOKEN month = TT_UNKNOWN;
+ TIME_TOKEN zone = TT_UNKNOWN;
+ int zone_offset = -1;
+ int date = -1;
+ PRInt32 year = -1;
+ int hour = -1;
+ int min = -1;
+ int sec = -1;
+
+ const char *rest = string;
+
+#ifdef DEBUG
+ int iterations = 0;
+#endif
+
+ PR_ASSERT(string && result);
+ if (!string || !result) return PR_FAILURE;
+
+ while (*rest)
+ {
+
+#ifdef DEBUG
+ if (iterations++ > 1000)
+ {
+ PR_ASSERT(0);
+ return PR_FAILURE;
+ }
+#endif
+
+ switch (*rest)
+ {
+ case 'a': case 'A':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'p' || rest[1] == 'P') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_APR;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 's') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_AST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'g' || rest[2] == 'G'))
+ month = TT_AUG;
+ break;
+ case 'b': case 'B':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_BST;
+ break;
+ case 'c': case 'C':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_CST;
+ break;
+ case 'd': case 'D':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'c' || rest[2] == 'C'))
+ month = TT_DEC;
+ break;
+ case 'e': case 'E':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EET;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_EST;
+ break;
+ case 'f': case 'F':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'b' || rest[2] == 'B'))
+ month = TT_FEB;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'r' || rest[1] == 'R') &&
+ (rest[2] == 'i' || rest[2] == 'I'))
+ dotw = TT_FRI;
+ break;
+ case 'g': case 'G':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'm' || rest[1] == 'M') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_GMT;
+ break;
+ case 'j': case 'J':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JAN;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_JST;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'l' || rest[2] == 'L'))
+ month = TT_JUL;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ month = TT_JUN;
+ break;
+ case 'm': case 'M':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'r' || rest[2] == 'R'))
+ month = TT_MAR;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 'y' || rest[2] == 'Y'))
+ month = TT_MAY;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MET;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_MON;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_MST;
+ break;
+ case 'n': case 'N':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'o' || rest[1] == 'O') &&
+ (rest[2] == 'v' || rest[2] == 'V'))
+ month = TT_NOV;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_NST;
+ break;
+ case 'o': case 'O':
+ if (month == TT_UNKNOWN &&
+ (rest[1] == 'c' || rest[1] == 'C') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ month = TT_OCT;
+ break;
+ case 'p': case 'P':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 'd' || rest[1] == 'D') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PDT;
+ else if (zone == TT_UNKNOWN &&
+ (rest[1] == 's' || rest[1] == 'S') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ zone = TT_PST;
+ break;
+ case 's': case 'S':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'a' || rest[1] == 'A') &&
+ (rest[2] == 't' || rest[2] == 'T'))
+ dotw = TT_SAT;
+ else if (month == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'p' || rest[2] == 'P'))
+ month = TT_SEP;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'n' || rest[2] == 'N'))
+ dotw = TT_SUN;
+ break;
+ case 't': case 'T':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'h' || rest[1] == 'H') &&
+ (rest[2] == 'u' || rest[2] == 'U'))
+ dotw = TT_THU;
+ else if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'u' || rest[1] == 'U') &&
+ (rest[2] == 'e' || rest[2] == 'E'))
+ dotw = TT_TUE;
+ break;
+ case 'u': case 'U':
+ if (zone == TT_UNKNOWN &&
+ (rest[1] == 't' || rest[1] == 'T') &&
+ !(rest[2] >= 'A' && rest[2] <= 'Z') &&
+ !(rest[2] >= 'a' && rest[2] <= 'z'))
+ /* UT is the same as GMT but UTx is not. */
+ zone = TT_GMT;
+ break;
+ case 'w': case 'W':
+ if (dotw == TT_UNKNOWN &&
+ (rest[1] == 'e' || rest[1] == 'E') &&
+ (rest[2] == 'd' || rest[2] == 'D'))
+ dotw = TT_WED;
+ break;
+
+ case '+': case '-':
+ {
+ const char *end;
+ int sign;
+ if (zone_offset != -1)
+ {
+ /* already got one... */
+ rest++;
+ break;
+ }
+ if (zone != TT_UNKNOWN && zone != TT_GMT)
+ {
+ /* GMT+0300 is legal, but PST+0300 is not. */
+ rest++;
+ break;
+ }
+
+ sign = ((*rest == '+') ? 1 : -1);
+ rest++; /* move over sign */
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+ if (rest == end) /* no digits here */
+ break;
+
+ if ((end - rest) == 4)
+ /* offset in HHMM */
+ zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) +
+ (((rest[2]-'0')*10) + (rest[3]-'0')));
+ else if ((end - rest) == 2)
+ /* offset in hours */
+ zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60;
+ else if ((end - rest) == 1)
+ /* offset in hours */
+ zone_offset = (rest[0]-'0') * 60;
+ else
+ /* 3 or >4 */
+ break;
+
+ zone_offset *= sign;
+ zone = TT_GMT;
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ int tmp_hour = -1;
+ int tmp_min = -1;
+ int tmp_sec = -1;
+ const char *end = rest + 1;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ /* end is now the first character after a range of digits. */
+
+ if (*end == ':')
+ {
+ if (hour >= 0 && min >= 0) /* already got it */
+ break;
+
+ /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */
+ if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_hour = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_hour = (rest[0]-'0');
+
+ /* move over the colon, and parse minutes */
+
+ rest = ++end;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after first colon? */
+ break;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_min = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_min = (rest[0]-'0');
+
+ /* now go for seconds */
+ rest = end;
+ if (*rest == ':')
+ rest++;
+ end = rest;
+ while (*end >= '0' && *end <= '9')
+ end++;
+
+ if (end == rest)
+ /* no digits after second colon - that's ok. */
+ ;
+ else if ((end - rest) > 2)
+ /* it is [0-9][0-9][0-9]+: */
+ break;
+ else if ((end - rest) == 2)
+ tmp_sec = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ else
+ tmp_sec = (rest[0]-'0');
+
+ /* If we made it here, we've parsed hour and min,
+ and possibly sec, so it worked as a unit. */
+
+ /* skip over whitespace and see if there's an AM or PM
+ directly following the time.
+ */
+ if (tmp_hour <= 12)
+ {
+ const char *s = end;
+ while (*s && (*s == ' ' || *s == '\t'))
+ s++;
+ if ((s[0] == 'p' || s[0] == 'P') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 10:05pm == 22:05, and 12:05pm == 12:05 */
+ tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12);
+ else if (tmp_hour == 12 &&
+ (s[0] == 'a' || s[0] == 'A') &&
+ (s[1] == 'm' || s[1] == 'M'))
+ /* 12:05am == 00:05 */
+ tmp_hour = 0;
+ }
+
+ hour = tmp_hour;
+ min = tmp_min;
+ sec = tmp_sec;
+ rest = end;
+ break;
+ }
+ else if ((*end == '/' || *end == '-') &&
+ end[1] >= '0' && end[1] <= '9')
+ {
+ /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95
+ or even 95-06-05...
+ #### But it doesn't handle 1995-06-22.
+ */
+ int n1, n2, n3;
+ const char *s;
+
+ if (month != TT_UNKNOWN)
+ /* if we saw a month name, this can't be. */
+ break;
+
+ s = rest;
+
+ n1 = (*s++ - '0'); /* first 1 or 2 digits */
+ if (*s >= '0' && *s <= '9')
+ n1 = n1*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* second 1 or 2 digits */
+ break;
+ n2 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n2 = n2*10 + (*s++ - '0');
+
+ if (*s != '/' && *s != '-') /* slash */
+ break;
+ s++;
+
+ if (*s < '0' || *s > '9') /* third 1, 2, or 4 digits */
+ break;
+ n3 = (*s++ - '0');
+ if (*s >= '0' && *s <= '9')
+ n3 = n3*10 + (*s++ - '0');
+
+ if (*s >= '0' && *s <= '9') /* optional digits 3 and 4 */
+ {
+ n3 = n3*10 + (*s++ - '0');
+ if (*s < '0' || *s > '9')
+ break;
+ n3 = n3*10 + (*s++ - '0');
+ }
+
+ if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */
+ (*s >= 'A' && *s <= 'Z') ||
+ (*s >= 'a' && *s <= 'z'))
+ break;
+
+ /* Ok, we parsed three 1-2 digit numbers, with / or -
+ between them. Now decide what the hell they are
+ (DD/MM/YY or MM/DD/YY or YY/MM/DD.)
+ */
+
+ if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */
+ {
+ if (n2 > 12) break;
+ if (n3 > 31) break;
+ year = n1;
+ if (year < 70)
+ year += 2000;
+ else if (year < 100)
+ year += 1900;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ date = n3;
+ rest = s;
+ break;
+ }
+
+ if (n1 > 12 && n2 > 12) /* illegal */
+ {
+ rest = s;
+ break;
+ }
+
+ if (n3 < 70)
+ n3 += 2000;
+ else if (n3 < 100)
+ n3 += 1900;
+
+ if (n1 > 12) /* must be DD/MM/YY */
+ {
+ date = n1;
+ month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1);
+ year = n3;
+ }
+ else /* assume MM/DD/YY */
+ {
+ /* #### In the ambiguous case, should we consult the
+ locale to find out the local default? */
+ month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1);
+ date = n2;
+ year = n3;
+ }
+ rest = s;
+ }
+ else if ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z'))
+ /* Digits followed by non-punctuation - what's that? */
+ ;
+ else if ((end - rest) == 4) /* four digits is a year */
+ year = (year < 0
+ ? ((rest[0]-'0')*1000L +
+ (rest[1]-'0')*100L +
+ (rest[2]-'0')*10L +
+ (rest[3]-'0'))
+ : year);
+ else if ((end - rest) == 2) /* two digits - date or year */
+ {
+ int n = ((rest[0]-'0')*10 +
+ (rest[1]-'0'));
+ /* If we don't have a date (day of the month) and we see a number
+ less than 32, then assume that is the date.
+
+ Otherwise, if we have a date and not a year, assume this is the
+ year. If it is less than 70, then assume it refers to the 21st
+ century. If it is two digits (>= 70), assume it refers to this
+ century. Otherwise, assume it refers to an unambiguous year.
+
+ The world will surely end soon.
+ */
+ if (date < 0 && n < 32)
+ date = n;
+ else if (year < 0)
+ {
+ if (n < 70)
+ year = 2000 + n;
+ else if (n < 100)
+ year = 1900 + n;
+ else
+ year = n;
+ }
+ /* else what the hell is this. */
+ }
+ else if ((end - rest) == 1) /* one digit - date */
+ date = (date < 0 ? (rest[0]-'0') : date);
+ /* else, three or more than four digits - what's that? */
+
+ break;
+ }
+ }
+
+ /* Skip to the end of this token, whether we parsed it or not.
+ Tokens are delimited by whitespace, or ,;-/
+ But explicitly not :+-.
+ */
+ while (*rest &&
+ *rest != ' ' && *rest != '\t' &&
+ *rest != ',' && *rest != ';' &&
+ *rest != '-' && *rest != '+' &&
+ *rest != '/' &&
+ *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']')
+ rest++;
+ /* skip over uninteresting chars. */
+ SKIP_MORE:
+ while (*rest &&
+ (*rest == ' ' || *rest == '\t' ||
+ *rest == ',' || *rest == ';' || *rest == '/' ||
+ *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']'))
+ rest++;
+
+ /* "-" is ignored at the beginning of a token if we have not yet
+ parsed a year (e.g., the second "-" in "30-AUG-1966"), or if
+ the character after the dash is not a digit. */
+ if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0)
+ || rest[1] < '0' || rest[1] > '9'))
+ {
+ rest++;
+ goto SKIP_MORE;
+ }
+
+ }
+
+ if (zone != TT_UNKNOWN && zone_offset == -1)
+ {
+ switch (zone)
+ {
+ case TT_PST: zone_offset = -8 * 60; break;
+ case TT_PDT: zone_offset = -7 * 60; break;
+ case TT_MST: zone_offset = -7 * 60; break;
+ case TT_MDT: zone_offset = -6 * 60; break;
+ case TT_CST: zone_offset = -6 * 60; break;
+ case TT_CDT: zone_offset = -5 * 60; break;
+ case TT_EST: zone_offset = -5 * 60; break;
+ case TT_EDT: zone_offset = -4 * 60; break;
+ case TT_AST: zone_offset = -4 * 60; break;
+ case TT_NST: zone_offset = -3 * 60 - 30; break;
+ case TT_GMT: zone_offset = 0 * 60; break;
+ case TT_BST: zone_offset = 1 * 60; break;
+ case TT_MET: zone_offset = 1 * 60; break;
+ case TT_EET: zone_offset = 2 * 60; break;
+ case TT_JST: zone_offset = 9 * 60; break;
+ default:
+ PR_ASSERT (0);
+ break;
+ }
+ }
+
+ /* If we didn't find a year, month, or day-of-the-month, we can't
+ possibly parse this, and in fact, mktime() will do something random
+ (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt
+ a numerologically significant date... */
+ if (month == TT_UNKNOWN || date == -1 || year == -1)
+ return PR_FAILURE;
+
+ memset(&tm, 0, sizeof(tm));
+ if (sec != -1)
+ tm.tm_sec = sec;
+ if (min != -1)
+ tm.tm_min = min;
+ if (hour != -1)
+ tm.tm_hour = hour;
+ if (date != -1)
+ tm.tm_mday = date;
+ if (month != TT_UNKNOWN)
+ tm.tm_month = (((int)month) - ((int)TT_JAN));
+ if (year != -1)
+ tm.tm_year = year;
+ if (dotw != TT_UNKNOWN)
+ tm.tm_wday = (((int)dotw) - ((int)TT_SUN));
+
+ if (zone == TT_UNKNOWN && default_to_gmt)
+ {
+ /* No zone was specified, so pretend the zone was GMT. */
+ zone = TT_GMT;
+ zone_offset = 0;
+ }
+
+ if (zone_offset == -1)
+ {
+ /* no zone was specified, and we're to assume that everything
+ is local. */
+ struct tm localTime;
+ time_t secs;
+
+ PR_ASSERT(tm.tm_month > -1
+ && tm.tm_mday > 0
+ && tm.tm_hour > -1
+ && tm.tm_min > -1
+ && tm.tm_sec > -1);
+
+ /*
+ * To obtain time_t from a tm structure representing the local
+ * time, we call mktime(). However, we need to see if we are
+ * on 1-Jan-1970 or before. If we are, we can't call mktime()
+ * because mktime() will crash on win16. In that case, we
+ * calculate zone_offset based on the zone offset at
+ * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the
+ * date we are parsing to transform the date to GMT. We also
+ * do so if mktime() returns (time_t) -1 (time out of range).
+ */
+
+ /* month, day, hours, mins and secs are always non-negative
+ so we dont need to worry about them. */
+ if(tm.tm_year >= 1970)
+ {
+ PRInt64 usec_per_sec;
+
+ localTime.tm_sec = tm.tm_sec;
+ localTime.tm_min = tm.tm_min;
+ localTime.tm_hour = tm.tm_hour;
+ localTime.tm_mday = tm.tm_mday;
+ localTime.tm_mon = tm.tm_month;
+ localTime.tm_year = tm.tm_year - 1900;
+ /* Set this to -1 to tell mktime "I don't care". If you set
+ it to 0 or 1, you are making assertions about whether the
+ date you are handing it is in daylight savings mode or not;
+ and if you're wrong, it will "fix" it for you. */
+ localTime.tm_isdst = -1;
+ secs = mktime(&localTime);
+ if (secs != (time_t) -1)
+ {
+#if defined(XP_MAC) && (__MSL__ < 0x6000)
+ /*
+ * The mktime() routine in MetroWerks MSL C
+ * Runtime library returns seconds since midnight,
+ * 1 Jan. 1900, not 1970 - in versions of MSL (Metrowerks Standard
+ * Library) prior to version 6. Only for older versions of
+ * MSL do we adjust the value of secs to the NSPR epoch
+ */
+ secs -= ((365 * 70UL) + 17) * 24 * 60 * 60;
+#endif
+ LL_I2L(*result, secs);
+ LL_I2L(usec_per_sec, PR_USEC_PER_SEC);
+ LL_MUL(*result, *result, usec_per_sec);
+ return PR_SUCCESS;
+ }
+ }
+
+ /* So mktime() can't handle this case. We assume the
+ zone_offset for the date we are parsing is the same as
+ the zone offset on 00:00:00 2 Jan 1970 GMT. */
+ secs = 86400;
+ (void) localtime_s(&localTime, &secs);
+ zone_offset = localTime.tm_min
+ + 60 * localTime.tm_hour
+ + 1440 * (localTime.tm_mday - 2);
+ }
+
+ tm.tm_params.tp_gmt_offset = zone_offset * 60;
+
+ *result = PR_ImplodeTime(&tm);
+
+ return PR_SUCCESS;
+} \ No newline at end of file
diff --git a/base/third_party/nspr/prtime.h b/base/third_party/nspr/prtime.h
new file mode 100644
index 0000000..70d25bc
--- /dev/null
+++ b/base/third_party/nspr/prtime.h
@@ -0,0 +1,185 @@
+/*
+* Portions are Copyright (C) 2007 Google Inc
+*
+* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is the Netscape Portable Runtime (NSPR).
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 1998-2000
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK *****
+*/
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * prtime.h --
+ *
+ * NSPR date and time functions
+ * CVS revision 3.11
+ * This file contains definitions of NSPR's basic types required by
+ * prtime.cc. These types have been copied over from the following NSPR
+ * files prtime.h, prtypes.h(CVS revision 3.35), prlong.h(CVS revision 3.13)
+ *
+ *---------------------------------------------------------------------------
+ */
+
+#ifndef BASE_PRTIME_H__
+#define BASE_PRTIME_H__
+
+#include "base/logging.h"
+#include "base/third_party/nspr/prtypes.h"
+
+#define PR_ASSERT DCHECK
+
+#define LL_I2L(l, i) ((l) = (PRInt64)(i))
+#define LL_MUL(r, a, b) ((r) = (a) * (b))
+
+/**********************************************************************/
+/************************* TYPES AND CONSTANTS ************************/
+/**********************************************************************/
+
+#define PR_MSEC_PER_SEC 1000UL
+#define PR_USEC_PER_SEC 1000000UL
+#define PR_NSEC_PER_SEC 1000000000UL
+#define PR_USEC_PER_MSEC 1000UL
+#define PR_NSEC_PER_MSEC 1000000UL
+
+/*
+ * PRTime --
+ *
+ * NSPR represents basic time as 64-bit signed integers relative
+ * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT).
+ * (GMT is also known as Coordinated Universal Time, UTC.)
+ * The units of time are in microseconds. Negative times are allowed
+ * to represent times prior to the January 1970 epoch. Such values are
+ * intended to be exported to other systems or converted to human
+ * readable form.
+ *
+ * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0
+ * simply uses PRInt64.
+ */
+
+typedef PRInt64 PRTime;
+
+/*
+ * Time zone and daylight saving time corrections applied to GMT to
+ * obtain the local time of some geographic location
+ */
+
+typedef struct PRTimeParameters {
+ PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */
+ PRInt32 tp_dst_offset; /* contribution of DST in seconds */
+} PRTimeParameters;
+
+/*
+ * PRExplodedTime --
+ *
+ * Time broken down into human-readable components such as year, month,
+ * day, hour, minute, second, and microsecond. Time zone and daylight
+ * saving time corrections may be applied. If they are applied, the
+ * offsets from the GMT must be saved in the 'tm_params' field so that
+ * all the information is available to reconstruct GMT.
+ *
+ * Notes on porting: PRExplodedTime corrresponds to struct tm in
+ * ANSI C, with the following differences:
+ * - an additional field tm_usec;
+ * - replacing tm_isdst by tm_params;
+ * - the month field is spelled tm_month, not tm_mon;
+ * - we use absolute year, AD, not the year since 1900.
+ * The corresponding type in NSPR 1.0 is called PRTime. Below is
+ * a table of date/time type correspondence in the three APIs:
+ * API time since epoch time in components
+ * ANSI C time_t struct tm
+ * NSPR 1.0 PRInt64 PRTime
+ * NSPR 2.0 PRTime PRExplodedTime
+ */
+
+typedef struct PRExplodedTime {
+ PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */
+ PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating
+ up to two leap seconds) */
+ PRInt32 tm_min; /* minutes past tm_hour (0-59) */
+ PRInt32 tm_hour; /* hours past tm_day (0-23) */
+ PRInt32 tm_mday; /* days past tm_mon (1-31, note that it
+ starts from 1) */
+ PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */
+ PRInt16 tm_year; /* absolute year, AD (note that we do not
+ count from 1900) */
+
+ PRInt8 tm_wday; /* calculated day of the week
+ (0-6, Sun = 0) */
+ PRInt16 tm_yday; /* calculated day of the year
+ (0-365, Jan 1 = 0) */
+
+ PRTimeParameters tm_params; /* time parameters used by conversion */
+} PRExplodedTime;
+
+NSPR_API(PRTime)
+PR_ImplodeTime(const PRExplodedTime *exploded);
+
+/*
+ * This parses a time/date string into a PRTime
+ * (microseconds after "1-Jan-1970 00:00:00 GMT").
+ * It returns PR_SUCCESS on success, and PR_FAILURE
+ * if the time/date string can't be parsed.
+ *
+ * Many formats are handled, including:
+ *
+ * 14 Apr 89 03:20:12
+ * 14 Apr 89 03:20 GMT
+ * Fri, 17 Mar 89 4:01:33
+ * Fri, 17 Mar 89 4:01 GMT
+ * Mon Jan 16 16:12 PDT 1989
+ * Mon Jan 16 16:12 +0130 1989
+ * 6 May 1992 16:41-JST (Wednesday)
+ * 22-AUG-1993 10:59:12.82
+ * 22-AUG-1993 10:59pm
+ * 22-AUG-1993 12:59am
+ * 22-AUG-1993 12:59 PM
+ * Friday, August 04, 1995 3:54 PM
+ * 06/21/95 04:24:34 PM
+ * 20/06/95 21:07
+ * 95-06-08 19:32:48 EDT
+ *
+ * If the input string doesn't contain a description of the timezone,
+ * we consult the `default_to_gmt' to decide whether the string should
+ * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE).
+ * The correct value for this argument depends on what standard specified
+ * the time string which you are parsing.
+ */
+
+NSPR_API(PRStatus) PR_ParseTimeString (
+ const char *string,
+ PRBool default_to_gmt,
+ PRTime *result);
+
+#endif // BASE_PRTIME_H__
diff --git a/base/third_party/nspr/prtypes.h b/base/third_party/nspr/prtypes.h
new file mode 100644
index 0000000..e9e41c2
--- /dev/null
+++ b/base/third_party/nspr/prtypes.h
@@ -0,0 +1,567 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape Portable Runtime (NSPR).
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1998-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+** File: prtypes.h
+** Description: Definitions of NSPR's basic types
+**
+** Prototypes and macros used to make up for deficiencies that we have found
+** in ANSI environments.
+**
+** Since we do not wrap <stdlib.h> and all the other standard headers, authors
+** of portable code will not know in general that they need these definitions.
+** Instead of requiring these authors to find the dependent uses in their code
+** and take the following steps only in those C files, we take steps once here
+** for all C files.
+**/
+
+#ifndef prtypes_h___
+#define prtypes_h___
+
+#ifdef MDCPUCFG
+#include MDCPUCFG
+#else
+#include "base/third_party/nspr/prcpucfg.h"
+#endif
+
+#include <stddef.h>
+
+/***********************************************************************
+** MACROS: PR_EXTERN
+** PR_IMPLEMENT
+** DESCRIPTION:
+** These are only for externally visible routines and globals. For
+** internal routines, just use "extern" for type checking and that
+** will not export internal cross-file or forward-declared symbols.
+** Define a macro for declaring procedures return types. We use this to
+** deal with windoze specific type hackery for DLL definitions. Use
+** PR_EXTERN when the prototype for the method is declared. Use
+** PR_IMPLEMENT for the implementation of the method.
+**
+** Example:
+** in dowhim.h
+** PR_EXTERN( void ) DoWhatIMean( void );
+** in dowhim.c
+** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; }
+**
+**
+***********************************************************************/
+#if defined(WIN32)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __type
+#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_BEOS)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(WIN16)
+
+#define PR_CALLBACK_DECL __cdecl
+
+#if defined(_WINDLL)
+#define PR_EXPORT(__type) extern __type _cdecl _export _loadds
+#define PR_IMPORT(__type) extern __type _cdecl _export _loadds
+#define PR_EXPORT_DATA(__type) extern __type _export
+#define PR_IMPORT_DATA(__type) extern __type _export
+
+#define PR_EXTERN(__type) extern __type _cdecl _export _loadds
+#define PR_IMPLEMENT(__type) __type _cdecl _export _loadds
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
+
+#else /* this must be .EXE */
+#define PR_EXPORT(__type) extern __type _cdecl _export
+#define PR_IMPORT(__type) extern __type _cdecl _export
+#define PR_EXPORT_DATA(__type) extern __type _export
+#define PR_IMPORT_DATA(__type) extern __type _export
+
+#define PR_EXTERN(__type) extern __type _cdecl _export
+#define PR_IMPLEMENT(__type) __type _cdecl _export
+#define PR_EXTERN_DATA(__type) extern __type _export
+#define PR_IMPLEMENT_DATA(__type) __type _export
+
+#define PR_CALLBACK __cdecl __loadds
+#define PR_STATIC_CALLBACK(__x) __x PR_CALLBACK
+#endif /* _WINDLL */
+
+#elif defined(XP_MAC)
+
+#define PR_EXPORT(__type) extern __declspec(export) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(export) __type
+#define PR_IMPORT(__type) extern __declspec(export) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(export) __type
+
+#define PR_EXTERN(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT(__type) __declspec(export) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(export) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(export) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2) && defined(__declspec)
+
+#define PR_EXPORT(__type) extern __declspec(dllexport) __type
+#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPORT(__type) extern __declspec(dllimport) __type
+#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type
+
+#define PR_EXTERN(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT(__type) __declspec(dllexport) __type
+#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type
+#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type
+
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#elif defined(XP_OS2_VACPP)
+
+#define PR_EXPORT(__type) extern __type
+#define PR_EXPORT_DATA(__type) extern __type
+#define PR_IMPORT(__type) extern __type
+#define PR_IMPORT_DATA(__type) extern __type
+
+#define PR_EXTERN(__type) extern __type
+#define PR_IMPLEMENT(__type) __type
+#define PR_EXTERN_DATA(__type) extern __type
+#define PR_IMPLEMENT_DATA(__type) __type
+#define PR_CALLBACK _Optlink
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK
+
+#else /* Unix */
+
+/* GCC 3.3 and later support the visibility attribute. */
+#if (__GNUC__ >= 4) || \
+ (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
+#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default")))
+#else
+#define PR_VISIBILITY_DEFAULT
+#endif
+
+#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+
+#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type
+#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type
+#define PR_CALLBACK
+#define PR_CALLBACK_DECL
+#define PR_STATIC_CALLBACK(__x) static __x
+
+#endif
+
+#if defined(_NSPR_BUILD_)
+#define NSPR_API(__type) PR_EXPORT(__type)
+#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type)
+#else
+#define NSPR_API(__type) PR_IMPORT(__type)
+#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type)
+#endif
+
+/***********************************************************************
+** MACROS: PR_BEGIN_MACRO
+** PR_END_MACRO
+** DESCRIPTION:
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+***********************************************************************/
+#define PR_BEGIN_MACRO do {
+#define PR_END_MACRO } while (0)
+
+/***********************************************************************
+** MACROS: PR_BEGIN_EXTERN_C
+** PR_END_EXTERN_C
+** DESCRIPTION:
+** Macro shorthands for conditional C++ extern block delimiters.
+***********************************************************************/
+#ifdef __cplusplus
+#define PR_BEGIN_EXTERN_C extern "C" {
+#define PR_END_EXTERN_C }
+#else
+#define PR_BEGIN_EXTERN_C
+#define PR_END_EXTERN_C
+#endif
+
+/***********************************************************************
+** MACROS: PR_BIT
+** PR_BITMASK
+** DESCRIPTION:
+** Bit masking macros. XXX n must be <= 31 to be portable
+***********************************************************************/
+#define PR_BIT(n) ((PRUint32)1 << (n))
+#define PR_BITMASK(n) (PR_BIT(n) - 1)
+
+/***********************************************************************
+** MACROS: PR_ROUNDUP
+** PR_MIN
+** PR_MAX
+** PR_ABS
+** DESCRIPTION:
+** Commonly used macros for operations on compatible types.
+***********************************************************************/
+#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y))
+#define PR_MIN(x,y) ((x)<(y)?(x):(y))
+#define PR_MAX(x,y) ((x)>(y)?(x):(y))
+#define PR_ABS(x) ((x)<0?-(x):(x))
+
+PR_BEGIN_EXTERN_C
+
+/************************************************************************
+** TYPES: PRUint8
+** PRInt8
+** DESCRIPTION:
+** The int8 types are known to be 8 bits each. There is no type that
+** is equivalent to a plain "char".
+************************************************************************/
+#if PR_BYTES_PER_BYTE == 1
+typedef unsigned char PRUint8;
+/*
+** Some cfront-based C++ compilers do not like 'signed char' and
+** issue the warning message:
+** warning: "signed" not implemented (ignored)
+** For these compilers, we have to define PRInt8 as plain 'char'.
+** Make sure that plain 'char' is indeed signed under these compilers.
+*/
+#if (defined(HPUX) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus < 199707L) \
+ || (defined(SCO) && defined(__cplusplus) \
+ && !defined(__GNUC__) && __cplusplus == 1L)
+typedef char PRInt8;
+#else
+typedef signed char PRInt8;
+#endif
+#else
+#error No suitable type for PRInt8/PRUint8
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT8_MAX
+ * PR_INT8_MIN
+ * PR_UINT8_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt8 or PRUint8.
+************************************************************************/
+
+#define PR_INT8_MAX 127
+#define PR_INT8_MIN (-128)
+#define PR_UINT8_MAX 255U
+
+/************************************************************************
+** TYPES: PRUint16
+** PRInt16
+** DESCRIPTION:
+** The int16 types are known to be 16 bits each.
+************************************************************************/
+#if PR_BYTES_PER_SHORT == 2
+typedef unsigned short PRUint16;
+typedef short PRInt16;
+#else
+#error No suitable type for PRInt16/PRUint16
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT16_MAX
+ * PR_INT16_MIN
+ * PR_UINT16_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt16 or PRUint16.
+************************************************************************/
+
+#define PR_INT16_MAX 32767
+#define PR_INT16_MIN (-32768)
+#define PR_UINT16_MAX 65535U
+
+/************************************************************************
+** TYPES: PRUint32
+** PRInt32
+** DESCRIPTION:
+** The int32 types are known to be 32 bits each.
+************************************************************************/
+#if PR_BYTES_PER_INT == 4
+typedef unsigned int PRUint32;
+typedef int PRInt32;
+#define PR_INT32(x) x
+#define PR_UINT32(x) x ## U
+#elif PR_BYTES_PER_LONG == 4
+typedef unsigned long PRUint32;
+typedef long PRInt32;
+#define PR_INT32(x) x ## L
+#define PR_UINT32(x) x ## UL
+#else
+#error No suitable type for PRInt32/PRUint32
+#endif
+
+/************************************************************************
+ * MACROS: PR_INT32_MAX
+ * PR_INT32_MIN
+ * PR_UINT32_MAX
+ * DESCRIPTION:
+ * The maximum and minimum values of a PRInt32 or PRUint32.
+************************************************************************/
+
+#define PR_INT32_MAX PR_INT32(2147483647)
+#define PR_INT32_MIN (-PR_INT32_MAX - 1)
+#define PR_UINT32_MAX PR_UINT32(4294967295)
+
+/************************************************************************
+** TYPES: PRUint64
+** PRInt64
+** DESCRIPTION:
+** The int64 types are known to be 64 bits each. Care must be used when
+** declaring variables of type PRUint64 or PRInt64. Different hardware
+** architectures and even different compilers have varying support for
+** 64 bit values. The only guaranteed portability requires the use of
+** the LL_ macros (see prlong.h).
+************************************************************************/
+#ifdef HAVE_LONG_LONG
+#if PR_BYTES_PER_LONG == 8
+typedef long PRInt64;
+typedef unsigned long PRUint64;
+#elif defined(WIN16)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#elif defined(WIN32) && !defined(__GNUC__)
+typedef __int64 PRInt64;
+typedef unsigned __int64 PRUint64;
+#else
+typedef long long PRInt64;
+typedef unsigned long long PRUint64;
+#endif /* PR_BYTES_PER_LONG == 8 */
+#else /* !HAVE_LONG_LONG */
+typedef struct {
+#ifdef IS_LITTLE_ENDIAN
+ PRUint32 lo, hi;
+#else
+ PRUint32 hi, lo;
+#endif
+} PRInt64;
+typedef PRInt64 PRUint64;
+#endif /* !HAVE_LONG_LONG */
+
+/************************************************************************
+** TYPES: PRUintn
+** PRIntn
+** DESCRIPTION:
+** The PRIntn types are most appropriate for automatic variables. They are
+** guaranteed to be at least 16 bits, though various architectures may
+** define them to be wider (e.g., 32 or even 64 bits). These types are
+** never valid for fields of a structure.
+************************************************************************/
+#if PR_BYTES_PER_INT >= 2
+typedef int PRIntn;
+typedef unsigned int PRUintn;
+#else
+#error 'sizeof(int)' not sufficient for platform use
+#endif
+
+/************************************************************************
+** TYPES: PRFloat64
+** DESCRIPTION:
+** NSPR's floating point type is always 64 bits.
+************************************************************************/
+typedef double PRFloat64;
+
+/************************************************************************
+** TYPES: PRSize
+** DESCRIPTION:
+** A type for representing the size of objects.
+************************************************************************/
+typedef size_t PRSize;
+
+
+/************************************************************************
+** TYPES: PROffset32, PROffset64
+** DESCRIPTION:
+** A type for representing byte offsets from some location.
+************************************************************************/
+typedef PRInt32 PROffset32;
+typedef PRInt64 PROffset64;
+
+/************************************************************************
+** TYPES: PRPtrDiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer subtraction.
+************************************************************************/
+typedef ptrdiff_t PRPtrdiff;
+
+/************************************************************************
+** TYPES: PRUptrdiff
+** DESCRIPTION:
+** A type for pointer difference. Variables of this type are suitable
+** for storing a pointer or pointer sutraction.
+************************************************************************/
+#ifdef _WIN64
+typedef unsigned __int64 PRUptrdiff;
+#else
+typedef unsigned long PRUptrdiff;
+#endif
+
+/************************************************************************
+** TYPES: PRBool
+** DESCRIPTION:
+** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE
+** for clarity of target type in assignments and actual arguments. Use
+** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans
+** just as you would C int-valued conditions.
+************************************************************************/
+typedef PRIntn PRBool;
+#define PR_TRUE 1
+#define PR_FALSE 0
+
+/************************************************************************
+** TYPES: PRPackedBool
+** DESCRIPTION:
+** Use PRPackedBool within structs where bitfields are not desirable
+** but minimum and consistant overhead matters.
+************************************************************************/
+typedef PRUint8 PRPackedBool;
+
+/*
+** Status code used by some routines that have a single point of failure or
+** special status return.
+*/
+typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus;
+
+#ifndef __PRUNICHAR__
+#define __PRUNICHAR__
+#if defined(WIN32) || defined(XP_MAC)
+typedef wchar_t PRUnichar;
+#else
+typedef PRUint16 PRUnichar;
+#endif
+#endif
+
+/*
+** WARNING: The undocumented data types PRWord and PRUword are
+** only used in the garbage collection and arena code. Do not
+** use PRWord and PRUword in new code.
+**
+** A PRWord is an integer that is the same size as a void*.
+** It implements the notion of a "word" in the Java Virtual
+** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine
+** Specification, Addison-Wesley, September 1996.
+** http://java.sun.com/docs/books/vmspec/index.html.)
+*/
+#ifdef _WIN64
+typedef __int64 PRWord;
+typedef unsigned __int64 PRUword;
+#else
+typedef long PRWord;
+typedef unsigned long PRUword;
+#endif
+
+#if defined(NO_NSPR_10_SUPPORT)
+#else
+/********* ???????????????? FIX ME ??????????????????????????? *****/
+/********************** Some old definitions until pr=>ds transition is done ***/
+/********************** Also, we are still using NSPR 1.0. GC ******************/
+/*
+** Fundamental NSPR macros, used nearly everywhere.
+*/
+
+#define PR_PUBLIC_API PR_IMPLEMENT
+
+/*
+** Macro body brackets so that macros with compound statement definitions
+** behave syntactically more like functions when called.
+*/
+#define NSPR_BEGIN_MACRO do {
+#define NSPR_END_MACRO } while (0)
+
+/*
+** Macro shorthands for conditional C++ extern block delimiters.
+*/
+#ifdef NSPR_BEGIN_EXTERN_C
+#undef NSPR_BEGIN_EXTERN_C
+#endif
+#ifdef NSPR_END_EXTERN_C
+#undef NSPR_END_EXTERN_C
+#endif
+
+#ifdef __cplusplus
+#define NSPR_BEGIN_EXTERN_C extern "C" {
+#define NSPR_END_EXTERN_C }
+#else
+#define NSPR_BEGIN_EXTERN_C
+#define NSPR_END_EXTERN_C
+#endif
+
+/********* ????????????? End Fix me ?????????????????????????????? *****/
+#endif /* NO_NSPR_10_SUPPORT */
+
+PR_END_EXTERN_C
+
+#if !defined(NO_NSPR_10_SUPPORT)
+#include "base/basictypes.h"
+#endif
+
+#endif /* prtypes_h___ */
+
diff --git a/base/third_party/nss/README.google b/base/third_party/nss/README.google
new file mode 100644
index 0000000..7106351
--- /dev/null
+++ b/base/third_party/nss/README.google
@@ -0,0 +1,8 @@
+The original code is the Network Security Services (NSS), licensed under
+the MPL/GPL/LGPL tri-license (http://www.mozilla.org/MPL/).
+
+We extracted the SHA-256 source files, eliminated unneeded dependencies,
+deleted or commented out unused code, and tweaked them for Chrome's source
+tree. sha512.c is renamed sha512.cc so that it can include Chrome's C++
+header "base/basictypes.h". We define NOUNROLL256 to reduce the object code
+size.
diff --git a/base/third_party/nss/blapi.h b/base/third_party/nss/blapi.h
new file mode 100644
index 0000000..6e57ee0
--- /dev/null
+++ b/base/third_party/nss/blapi.h
@@ -0,0 +1,101 @@
+/*
+ * crypto.h - public data structures and prototypes for the crypto library
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: blapi.h,v 1.27 2007/11/09 18:49:32 wtc%google.com Exp $ */
+
+#ifndef _BLAPI_H_
+#define _BLAPI_H_
+
+#include "base/third_party/nss/blapit.h"
+
+/******************************************/
+
+extern SHA256Context *SHA256_NewContext(void);
+extern void SHA256_DestroyContext(SHA256Context *cx, PRBool freeit);
+extern void SHA256_Begin(SHA256Context *cx);
+extern void SHA256_Update(SHA256Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA256_End(SHA256Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length);
+extern SECStatus SHA256_Hash(unsigned char *dest, const char *src);
+extern void SHA256_TraceState(SHA256Context *cx);
+extern unsigned int SHA256_FlattenSize(SHA256Context *cx);
+extern SECStatus SHA256_Flatten(SHA256Context *cx,unsigned char *space);
+extern SHA256Context * SHA256_Resurrect(unsigned char *space, void *arg);
+extern void SHA256_Clone(SHA256Context *dest, SHA256Context *src);
+
+/******************************************/
+
+extern SHA512Context *SHA512_NewContext(void);
+extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit);
+extern void SHA512_Begin(SHA512Context *cx);
+extern void SHA512_Update(SHA512Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA512_End(SHA512Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length);
+extern SECStatus SHA512_Hash(unsigned char *dest, const char *src);
+extern void SHA512_TraceState(SHA512Context *cx);
+extern unsigned int SHA512_FlattenSize(SHA512Context *cx);
+extern SECStatus SHA512_Flatten(SHA512Context *cx,unsigned char *space);
+extern SHA512Context * SHA512_Resurrect(unsigned char *space, void *arg);
+extern void SHA512_Clone(SHA512Context *dest, SHA512Context *src);
+
+/******************************************/
+
+extern SHA384Context *SHA384_NewContext(void);
+extern void SHA384_DestroyContext(SHA384Context *cx, PRBool freeit);
+extern void SHA384_Begin(SHA384Context *cx);
+extern void SHA384_Update(SHA384Context *cx, const unsigned char *input,
+ unsigned int inputLen);
+extern void SHA384_End(SHA384Context *cx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen);
+extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length);
+extern SECStatus SHA384_Hash(unsigned char *dest, const char *src);
+extern void SHA384_TraceState(SHA384Context *cx);
+extern unsigned int SHA384_FlattenSize(SHA384Context *cx);
+extern SECStatus SHA384_Flatten(SHA384Context *cx,unsigned char *space);
+extern SHA384Context * SHA384_Resurrect(unsigned char *space, void *arg);
+extern void SHA384_Clone(SHA384Context *dest, SHA384Context *src);
+
+#endif /* _BLAPI_H_ */
diff --git a/base/third_party/nss/blapit.h b/base/third_party/nss/blapit.h
new file mode 100644
index 0000000..e16a084
--- /dev/null
+++ b/base/third_party/nss/blapit.h
@@ -0,0 +1,91 @@
+/*
+ * blapit.h - public data structures for the crypto library
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Dr Vipul Gupta <vipul.gupta@sun.com> and
+ * Douglas Stebila <douglas@stebila.ca>, Sun Microsystems Laboratories
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: blapit.h,v 1.20 2007/02/28 19:47:37 rrelyea%redhat.com Exp $ */
+
+#ifndef _BLAPIT_H_
+#define _BLAPIT_H_
+
+#include "base/third_party/nspr/prtypes.h"
+
+/*
+** A status code. Status's are used by procedures that return status
+** values. Again the motivation is so that a compiler can generate
+** warnings when return values are wrong. Correct testing of status codes:
+**
+** SECStatus rv;
+** rv = some_function (some_argument);
+** if (rv != SECSuccess)
+** do_an_error_thing();
+**
+*/
+typedef enum _SECStatus {
+ SECWouldBlock = -2,
+ SECFailure = -1,
+ SECSuccess = 0
+} SECStatus;
+
+#define SHA256_LENGTH 32 /* bytes */
+#define SHA384_LENGTH 48 /* bytes */
+#define SHA512_LENGTH 64 /* bytes */
+#define HASH_LENGTH_MAX SHA512_LENGTH
+
+/*
+ * Input block size for each hash algorithm.
+ */
+
+#define SHA256_BLOCK_LENGTH 64 /* bytes */
+#define SHA384_BLOCK_LENGTH 128 /* bytes */
+#define SHA512_BLOCK_LENGTH 128 /* bytes */
+#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH
+
+/***************************************************************************
+** Opaque objects
+*/
+
+struct SHA256ContextStr ;
+struct SHA512ContextStr ;
+
+typedef struct SHA256ContextStr SHA256Context;
+typedef struct SHA512ContextStr SHA512Context;
+/* SHA384Context is really a SHA512ContextStr. This is not a mistake. */
+typedef struct SHA512ContextStr SHA384Context;
+
+#endif /* _BLAPIT_H_ */
diff --git a/base/third_party/nss/sha256.h b/base/third_party/nss/sha256.h
new file mode 100644
index 0000000..e641b49
--- /dev/null
+++ b/base/third_party/nss/sha256.h
@@ -0,0 +1,51 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _SHA_256_H_
+#define _SHA_256_H_
+
+#include "base/third_party/nspr/prtypes.h"
+
+struct SHA256ContextStr {
+ union {
+ PRUint32 w[64]; /* message schedule, input buffer, plus 48 words */
+ PRUint8 b[256];
+ } u;
+ PRUint32 h[8]; /* 8 state variables */
+ PRUint32 sizeHi,sizeLo; /* 64-bit count of hashed bytes. */
+};
+
+#endif /* _SHA_256_H_ */
diff --git a/base/third_party/nss/sha512.cc b/base/third_party/nss/sha512.cc
new file mode 100644
index 0000000..d17f532c
--- /dev/null
+++ b/base/third_party/nss/sha512.cc
@@ -0,0 +1,1396 @@
+/*
+ * sha512.c - implementation of SHA256, SHA384 and SHA512
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */
+
+// Prevent manual unrolling in the sha256 code, which reduces the binary code
+// size from ~10k to ~1k. The performance should be reasonable for our use.
+#define NOUNROLL256 1
+
+#if defined(_M_IX86) || defined(i386) || defined(__i386) || defined(__i386__)
+#define _X86_ 1
+#endif
+
+#include "base/third_party/nspr/prcpucfg.h"
+#if defined(_X86_) || defined(SHA_NO_LONG_LONG)
+#define NOUNROLL512 1
+#undef HAVE_LONG_LONG
+#endif
+#include "base/third_party/nspr/prtypes.h" /* for PRUintXX */
+#include "base/third_party/nss/blapi.h"
+#include "base/third_party/nss/sha256.h" /* for struct SHA256ContextStr */
+
+#include <stdlib.h>
+#include <string.h>
+#define PORT_New(type) static_cast<type*>(malloc(sizeof(type)))
+#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0)
+#define PORT_Strlen(s) static_cast<uint32>(strlen(s))
+#define PORT_Memcpy memcpy
+
+/* ============= Common constants and defines ======================= */
+
+#define W ctx->u.w
+#define B ctx->u.b
+#define H ctx->h
+
+#define SHR(x,n) (x >> n)
+#define SHL(x,n) (x << n)
+#define Ch(x,y,z) ((x & y) ^ (~x & z))
+#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z))
+
+/* Padding used with all flavors of SHA */
+static const PRUint8 pad[240] = {
+0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ /* compiler will fill the rest in with zeros */
+};
+
+/* ============= SHA256 implemenmtation ================================== */
+
+/* SHA-256 constants, K256. */
+static const PRUint32 K256[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+};
+
+/* SHA-256 initial hash values */
+static const PRUint32 H256[8] = {
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
+};
+
+#if defined(_MSC_VER) && defined(_X86_)
+#ifndef FORCEINLINE
+#if (_MSC_VER >= 1200)
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE __inline
+#endif
+#endif
+#define FASTCALL __fastcall
+
+static FORCEINLINE PRUint32 FASTCALL
+swap4b(PRUint32 dwd)
+{
+ __asm {
+ mov eax,dwd
+ bswap eax
+ }
+}
+
+#define SHA_HTONL(x) swap4b(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#elif defined(LINUX) && defined(_X86_)
+#undef __OPTIMIZE__
+#define __OPTIMIZE__ 1
+#undef __pentium__
+#define __pentium__ 1
+#include <byteswap.h>
+#define SHA_HTONL(x) bswap_32(x)
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+
+#else /* neither windows nor Linux PC */
+#define SWAP4MASK 0x00FF00FF
+#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \
+ ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK))
+#define BYTESWAP4(x) x = SHA_HTONL(x)
+#endif
+
+#if defined(_MSC_VER) && defined(_X86_)
+#pragma intrinsic (_lrotr, _lrotl)
+#define ROTR32(x,n) _lrotr(x,n)
+#define ROTL32(x,n) _lrotl(x,n)
+#else
+#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n)))
+#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n)))
+#endif
+
+/* Capitol Sigma and lower case sigma functions */
+#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22))
+#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25))
+#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3))
+#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10))
+
+SHA256Context *
+SHA256_NewContext(void)
+{
+ SHA256Context *ctx = PORT_New(SHA256Context);
+ return ctx;
+}
+
+void
+SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA256_Begin(SHA256Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H256, sizeof H256);
+}
+
+static void
+SHA256_Compress(SHA256Context *ctx)
+{
+ {
+ register PRUint32 t1, t2;
+
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(W[0]);
+ BYTESWAP4(W[1]);
+ BYTESWAP4(W[2]);
+ BYTESWAP4(W[3]);
+ BYTESWAP4(W[4]);
+ BYTESWAP4(W[5]);
+ BYTESWAP4(W[6]);
+ BYTESWAP4(W[7]);
+ BYTESWAP4(W[8]);
+ BYTESWAP4(W[9]);
+ BYTESWAP4(W[10]);
+ BYTESWAP4(W[11]);
+ BYTESWAP4(W[12]);
+ BYTESWAP4(W[13]);
+ BYTESWAP4(W[14]);
+ BYTESWAP4(W[15]);
+#endif
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+ /* prepare the "message schedule" */
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 16; t < 64; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+
+#endif
+#undef INITW
+ }
+ {
+ PRUint32 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c);
+
+#ifdef NOUNROLL256
+ {
+ int t;
+ for (t = 0; t < 64; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+#endif
+
+ H[0] += a;
+ H[1] += b;
+ H[2] += c;
+ H[3] += d;
+ H[4] += e;
+ H[5] += f;
+ H[6] += g;
+ H[7] += h;
+ }
+#undef ROUND
+}
+
+#undef s0
+#undef s1
+#undef S0
+#undef S1
+
+void
+SHA256_Update(SHA256Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ if (!inputLen)
+ return;
+
+ /* Add inputLen into the count of bytes processed, before processing */
+ if ((ctx->sizeLo += inputLen) < inputLen)
+ ctx->sizeHi++;
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA256_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA256_BLOCK_LENGTH)
+ SHA256_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA256_BLOCK_LENGTH) {
+ memcpy(B, input, SHA256_BLOCK_LENGTH);
+ input += SHA256_BLOCK_LENGTH;
+ inputLen -= SHA256_BLOCK_LENGTH;
+ SHA256_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA256_End(SHA256Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+ unsigned int inBuf = ctx->sizeLo & 0x3f;
+ unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf);
+ PRUint32 hi, lo;
+#ifdef SWAP4MASK
+ PRUint32 t1;
+#endif
+
+ hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29);
+ lo = (ctx->sizeLo << 3);
+
+ SHA256_Update(ctx, pad, padLen);
+
+#if defined(IS_LITTLE_ENDIAN)
+ W[14] = SHA_HTONL(hi);
+ W[15] = SHA_HTONL(lo);
+#else
+ W[14] = hi;
+ W[15] = lo;
+#endif
+ SHA256_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP4(H[0]);
+ BYTESWAP4(H[1]);
+ BYTESWAP4(H[2]);
+ BYTESWAP4(H[3]);
+ BYTESWAP4(H[4]);
+ BYTESWAP4(H[5]);
+ BYTESWAP4(H[6]);
+ BYTESWAP4(H[7]);
+#endif
+ padLen = PR_MIN(SHA256_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */
+#if 0
+SECStatus
+SHA256_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length)
+{
+ SHA256Context ctx;
+ unsigned int outLen;
+
+ SHA256_Begin(&ctx);
+ SHA256_Update(&ctx, src, src_length);
+ SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA256_Hash(unsigned char *dest, const char *src)
+{
+ return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA256_TraceState(SHA256Context *ctx) { }
+
+unsigned int
+SHA256_FlattenSize(SHA256Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA256_Flatten(SHA256Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA256Context *
+SHA256_Resurrect(unsigned char *space, void *arg)
+{
+ SHA256Context *ctx = SHA256_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+void SHA256_Clone(SHA256Context *dest, SHA256Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+
+/* ======= SHA512 and SHA384 common constants and defines ================= */
+
+/* common #defines for SHA512 and SHA384 */
+#if defined(HAVE_LONG_LONG)
+#define ROTR64(x,n) ((x >> n) | (x << (64 - n)))
+#define ROTL64(x,n) ((x << n) | (x >> (64 - n)))
+
+#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39))
+#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41))
+#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7))
+#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6))
+
+#if PR_BYTES_PER_LONG == 8
+#define ULLC(hi,lo) 0x ## hi ## lo ## UL
+#elif defined(_MSC_VER)
+#define ULLC(hi,lo) 0x ## hi ## lo ## ui64
+#else
+#define ULLC(hi,lo) 0x ## hi ## lo ## ULL
+#endif
+
+#define SHA_MASK16 ULLC(0000FFFF,0000FFFF)
+#define SHA_MASK8 ULLC(00FF00FF,00FF00FF)
+#define SHA_HTONLL(x) (t1 = x, \
+ t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \
+ t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \
+ (t1 >> 32) | (t1 << 32))
+#define BYTESWAP8(x) x = SHA_HTONLL(x)
+
+#else /* no long long */
+
+#if defined(IS_LITTLE_ENDIAN)
+#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U }
+#else
+#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U }
+#endif
+
+#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \
+ x.hi ^= x.lo ^= x.hi ^= x.lo, x)
+#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \
+ tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0)
+#endif
+
+/* SHA-384 and SHA-512 constants, K512. */
+static const PRUint64 K512[80] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL ,
+ 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL ,
+ 0x3956c25bf348b538UL , 0x59f111f1b605d019UL ,
+ 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL ,
+ 0xd807aa98a3030242UL , 0x12835b0145706fbeUL ,
+ 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL ,
+ 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL ,
+ 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL ,
+ 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL ,
+ 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL ,
+ 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL ,
+ 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL ,
+ 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL ,
+ 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL ,
+ 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL ,
+ 0x06ca6351e003826fUL , 0x142929670a0e6e70UL ,
+ 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL ,
+ 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL ,
+ 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL ,
+ 0x81c2c92e47edaee6UL , 0x92722c851482353bUL ,
+ 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL ,
+ 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL ,
+ 0xd192e819d6ef5218UL , 0xd69906245565a910UL ,
+ 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL ,
+ 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL ,
+ 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL ,
+ 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL ,
+ 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL ,
+ 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL ,
+ 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL ,
+ 0x90befffa23631e28UL , 0xa4506cebde82bde9UL ,
+ 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL ,
+ 0xca273eceea26619cUL , 0xd186b8c721c0c207UL ,
+ 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL ,
+ 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL ,
+ 0x113f9804bef90daeUL , 0x1b710b35131c471bUL ,
+ 0x28db77f523047d84UL , 0x32caab7b40c72493UL ,
+ 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL ,
+ 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL ,
+ 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL
+#else
+ ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd),
+ ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc),
+ ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019),
+ ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118),
+ ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe),
+ ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2),
+ ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1),
+ ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694),
+ ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3),
+ ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65),
+ ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483),
+ ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5),
+ ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210),
+ ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4),
+ ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725),
+ ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70),
+ ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926),
+ ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df),
+ ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8),
+ ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b),
+ ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001),
+ ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30),
+ ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910),
+ ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8),
+ ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53),
+ ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8),
+ ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb),
+ ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3),
+ ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60),
+ ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec),
+ ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9),
+ ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b),
+ ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207),
+ ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178),
+ ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6),
+ ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b),
+ ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493),
+ ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c),
+ ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a),
+ ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817)
+#endif
+};
+
+struct SHA512ContextStr {
+ union {
+ PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */
+ PRUint32 l[160];
+ PRUint8 b[640];
+ } u;
+ PRUint64 h[8]; /* 8 state variables */
+ PRUint64 sizeLo; /* 64-bit count of hashed bytes. */
+};
+
+/* =========== SHA512 implementation ===================================== */
+
+/* SHA-512 initial hash values */
+static const PRUint64 H512[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL ,
+ 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL ,
+ 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL ,
+ 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL
+#else
+ ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b),
+ ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1),
+ ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f),
+ ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179)
+#endif
+};
+
+
+SHA512Context *
+SHA512_NewContext(void)
+{
+ SHA512Context *ctx = PORT_New(SHA512Context);
+ return ctx;
+}
+
+void
+SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit)
+{
+ if (freeit) {
+ PORT_ZFree(ctx, sizeof *ctx);
+ }
+}
+
+void
+SHA512_Begin(SHA512Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H512, sizeof H512);
+}
+
+#if defined(SHA512_TRACE)
+#if defined(HAVE_LONG_LONG)
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \
+ n, #e, d, #a, h);
+#else
+#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \
+ n, #e, d.hi, d.lo, #a, h.hi, h.lo);
+#endif
+#else
+#define DUMP(n,a,d,e,h)
+#endif
+
+#if defined(HAVE_LONG_LONG)
+
+#define ADDTO(x,y) y += x
+
+#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16])
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \
+ d += h; \
+ h += S0(a) + Maj(a,b,c); \
+ DUMP(n,a,d,e,h)
+
+#else /* use only 32-bit variables, and don't unroll loops */
+
+#undef NOUNROLL512
+#define NOUNROLL512 1
+
+#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo)
+
+#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32))
+#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n))
+
+/* Capitol Sigma and lower case sigma functions */
+#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi))
+#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7))
+
+#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi))
+#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6))
+
+#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi))
+#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo))
+
+#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi))
+#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo))
+
+/* 32-bit versions of Ch and Maj */
+#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo))
+#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo))
+
+#define INITW(t) \
+ do { \
+ PRUint32 lo, tm; \
+ PRUint32 cy = 0; \
+ lo = s1lo(W[t-2]); \
+ lo += (tm = W[t-7].lo); if (lo < tm) cy++; \
+ lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \
+ lo += (tm = W[t-16].lo); if (lo < tm) cy++; \
+ W[t].lo = lo; \
+ W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \
+ } while (0)
+
+#define ROUND(n,a,b,c,d,e,f,g,h) \
+ { \
+ PRUint32 lo, tm, cy; \
+ lo = S1lo(e); \
+ lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \
+ lo += (tm = K512[n].lo); if (lo < tm) cy++; \
+ lo += (tm = W[n].lo); if (lo < tm) cy++; \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \
+ d.lo += h.lo; \
+ d.hi += h.hi + (d.lo < h.lo); \
+ lo = S0lo(a); \
+ lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \
+ h.lo += lo; if (h.lo < lo) cy++; \
+ h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \
+ DUMP(n,a,d,e,h) \
+ }
+#endif
+
+static void
+SHA512_Compress(SHA512Context *ctx)
+{
+#if defined(IS_LITTLE_ENDIAN)
+ {
+#if defined(HAVE_LONG_LONG)
+ PRUint64 t1;
+#else
+ PRUint32 t1;
+#endif
+ BYTESWAP8(W[0]);
+ BYTESWAP8(W[1]);
+ BYTESWAP8(W[2]);
+ BYTESWAP8(W[3]);
+ BYTESWAP8(W[4]);
+ BYTESWAP8(W[5]);
+ BYTESWAP8(W[6]);
+ BYTESWAP8(W[7]);
+ BYTESWAP8(W[8]);
+ BYTESWAP8(W[9]);
+ BYTESWAP8(W[10]);
+ BYTESWAP8(W[11]);
+ BYTESWAP8(W[12]);
+ BYTESWAP8(W[13]);
+ BYTESWAP8(W[14]);
+ BYTESWAP8(W[15]);
+ }
+#endif
+
+ {
+ PRUint64 t1, t2;
+#ifdef NOUNROLL512
+ {
+ /* prepare the "message schedule" */
+ int t;
+ for (t = 16; t < 80; ++t) {
+ INITW(t);
+ }
+ }
+#else
+ INITW(16);
+ INITW(17);
+ INITW(18);
+ INITW(19);
+
+ INITW(20);
+ INITW(21);
+ INITW(22);
+ INITW(23);
+ INITW(24);
+ INITW(25);
+ INITW(26);
+ INITW(27);
+ INITW(28);
+ INITW(29);
+
+ INITW(30);
+ INITW(31);
+ INITW(32);
+ INITW(33);
+ INITW(34);
+ INITW(35);
+ INITW(36);
+ INITW(37);
+ INITW(38);
+ INITW(39);
+
+ INITW(40);
+ INITW(41);
+ INITW(42);
+ INITW(43);
+ INITW(44);
+ INITW(45);
+ INITW(46);
+ INITW(47);
+ INITW(48);
+ INITW(49);
+
+ INITW(50);
+ INITW(51);
+ INITW(52);
+ INITW(53);
+ INITW(54);
+ INITW(55);
+ INITW(56);
+ INITW(57);
+ INITW(58);
+ INITW(59);
+
+ INITW(60);
+ INITW(61);
+ INITW(62);
+ INITW(63);
+ INITW(64);
+ INITW(65);
+ INITW(66);
+ INITW(67);
+ INITW(68);
+ INITW(69);
+
+ INITW(70);
+ INITW(71);
+ INITW(72);
+ INITW(73);
+ INITW(74);
+ INITW(75);
+ INITW(76);
+ INITW(77);
+ INITW(78);
+ INITW(79);
+#endif
+ }
+#ifdef SHA512_TRACE
+ {
+ int i;
+ for (i = 0; i < 80; ++i) {
+#ifdef HAVE_LONG_LONG
+ printf("W[%2d] = %016lx\n", i, W[i]);
+#else
+ printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo);
+#endif
+ }
+ }
+#endif
+ {
+ PRUint64 a, b, c, d, e, f, g, h;
+
+ a = H[0];
+ b = H[1];
+ c = H[2];
+ d = H[3];
+ e = H[4];
+ f = H[5];
+ g = H[6];
+ h = H[7];
+
+#ifdef NOUNROLL512
+ {
+ int t;
+ for (t = 0; t < 80; t+= 8) {
+ ROUND(t+0,a,b,c,d,e,f,g,h)
+ ROUND(t+1,h,a,b,c,d,e,f,g)
+ ROUND(t+2,g,h,a,b,c,d,e,f)
+ ROUND(t+3,f,g,h,a,b,c,d,e)
+ ROUND(t+4,e,f,g,h,a,b,c,d)
+ ROUND(t+5,d,e,f,g,h,a,b,c)
+ ROUND(t+6,c,d,e,f,g,h,a,b)
+ ROUND(t+7,b,c,d,e,f,g,h,a)
+ }
+ }
+#else
+ ROUND( 0,a,b,c,d,e,f,g,h)
+ ROUND( 1,h,a,b,c,d,e,f,g)
+ ROUND( 2,g,h,a,b,c,d,e,f)
+ ROUND( 3,f,g,h,a,b,c,d,e)
+ ROUND( 4,e,f,g,h,a,b,c,d)
+ ROUND( 5,d,e,f,g,h,a,b,c)
+ ROUND( 6,c,d,e,f,g,h,a,b)
+ ROUND( 7,b,c,d,e,f,g,h,a)
+
+ ROUND( 8,a,b,c,d,e,f,g,h)
+ ROUND( 9,h,a,b,c,d,e,f,g)
+ ROUND(10,g,h,a,b,c,d,e,f)
+ ROUND(11,f,g,h,a,b,c,d,e)
+ ROUND(12,e,f,g,h,a,b,c,d)
+ ROUND(13,d,e,f,g,h,a,b,c)
+ ROUND(14,c,d,e,f,g,h,a,b)
+ ROUND(15,b,c,d,e,f,g,h,a)
+
+ ROUND(16,a,b,c,d,e,f,g,h)
+ ROUND(17,h,a,b,c,d,e,f,g)
+ ROUND(18,g,h,a,b,c,d,e,f)
+ ROUND(19,f,g,h,a,b,c,d,e)
+ ROUND(20,e,f,g,h,a,b,c,d)
+ ROUND(21,d,e,f,g,h,a,b,c)
+ ROUND(22,c,d,e,f,g,h,a,b)
+ ROUND(23,b,c,d,e,f,g,h,a)
+
+ ROUND(24,a,b,c,d,e,f,g,h)
+ ROUND(25,h,a,b,c,d,e,f,g)
+ ROUND(26,g,h,a,b,c,d,e,f)
+ ROUND(27,f,g,h,a,b,c,d,e)
+ ROUND(28,e,f,g,h,a,b,c,d)
+ ROUND(29,d,e,f,g,h,a,b,c)
+ ROUND(30,c,d,e,f,g,h,a,b)
+ ROUND(31,b,c,d,e,f,g,h,a)
+
+ ROUND(32,a,b,c,d,e,f,g,h)
+ ROUND(33,h,a,b,c,d,e,f,g)
+ ROUND(34,g,h,a,b,c,d,e,f)
+ ROUND(35,f,g,h,a,b,c,d,e)
+ ROUND(36,e,f,g,h,a,b,c,d)
+ ROUND(37,d,e,f,g,h,a,b,c)
+ ROUND(38,c,d,e,f,g,h,a,b)
+ ROUND(39,b,c,d,e,f,g,h,a)
+
+ ROUND(40,a,b,c,d,e,f,g,h)
+ ROUND(41,h,a,b,c,d,e,f,g)
+ ROUND(42,g,h,a,b,c,d,e,f)
+ ROUND(43,f,g,h,a,b,c,d,e)
+ ROUND(44,e,f,g,h,a,b,c,d)
+ ROUND(45,d,e,f,g,h,a,b,c)
+ ROUND(46,c,d,e,f,g,h,a,b)
+ ROUND(47,b,c,d,e,f,g,h,a)
+
+ ROUND(48,a,b,c,d,e,f,g,h)
+ ROUND(49,h,a,b,c,d,e,f,g)
+ ROUND(50,g,h,a,b,c,d,e,f)
+ ROUND(51,f,g,h,a,b,c,d,e)
+ ROUND(52,e,f,g,h,a,b,c,d)
+ ROUND(53,d,e,f,g,h,a,b,c)
+ ROUND(54,c,d,e,f,g,h,a,b)
+ ROUND(55,b,c,d,e,f,g,h,a)
+
+ ROUND(56,a,b,c,d,e,f,g,h)
+ ROUND(57,h,a,b,c,d,e,f,g)
+ ROUND(58,g,h,a,b,c,d,e,f)
+ ROUND(59,f,g,h,a,b,c,d,e)
+ ROUND(60,e,f,g,h,a,b,c,d)
+ ROUND(61,d,e,f,g,h,a,b,c)
+ ROUND(62,c,d,e,f,g,h,a,b)
+ ROUND(63,b,c,d,e,f,g,h,a)
+
+ ROUND(64,a,b,c,d,e,f,g,h)
+ ROUND(65,h,a,b,c,d,e,f,g)
+ ROUND(66,g,h,a,b,c,d,e,f)
+ ROUND(67,f,g,h,a,b,c,d,e)
+ ROUND(68,e,f,g,h,a,b,c,d)
+ ROUND(69,d,e,f,g,h,a,b,c)
+ ROUND(70,c,d,e,f,g,h,a,b)
+ ROUND(71,b,c,d,e,f,g,h,a)
+
+ ROUND(72,a,b,c,d,e,f,g,h)
+ ROUND(73,h,a,b,c,d,e,f,g)
+ ROUND(74,g,h,a,b,c,d,e,f)
+ ROUND(75,f,g,h,a,b,c,d,e)
+ ROUND(76,e,f,g,h,a,b,c,d)
+ ROUND(77,d,e,f,g,h,a,b,c)
+ ROUND(78,c,d,e,f,g,h,a,b)
+ ROUND(79,b,c,d,e,f,g,h,a)
+#endif
+
+ ADDTO(a,H[0]);
+ ADDTO(b,H[1]);
+ ADDTO(c,H[2]);
+ ADDTO(d,H[3]);
+ ADDTO(e,H[4]);
+ ADDTO(f,H[5]);
+ ADDTO(g,H[6]);
+ ADDTO(h,H[7]);
+ }
+}
+
+void
+SHA512_Update(SHA512Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ unsigned int inBuf;
+ if (!inputLen)
+ return;
+
+#if defined(HAVE_LONG_LONG)
+ inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ /* Add inputLen into the count of bytes processed, before processing */
+ ctx->sizeLo += inputLen;
+#else
+ inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ ctx->sizeLo.lo += inputLen;
+ if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++;
+#endif
+
+ /* if data already in buffer, attemp to fill rest of buffer */
+ if (inBuf) {
+ unsigned int todo = SHA512_BLOCK_LENGTH - inBuf;
+ if (inputLen < todo)
+ todo = inputLen;
+ memcpy(B + inBuf, input, todo);
+ input += todo;
+ inputLen -= todo;
+ if (inBuf + todo == SHA512_BLOCK_LENGTH)
+ SHA512_Compress(ctx);
+ }
+
+ /* if enough data to fill one or more whole buffers, process them. */
+ while (inputLen >= SHA512_BLOCK_LENGTH) {
+ memcpy(B, input, SHA512_BLOCK_LENGTH);
+ input += SHA512_BLOCK_LENGTH;
+ inputLen -= SHA512_BLOCK_LENGTH;
+ SHA512_Compress(ctx);
+ }
+ /* if data left over, fill it into buffer */
+ if (inputLen)
+ memcpy(B, input, inputLen);
+}
+
+void
+SHA512_End(SHA512Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#if defined(HAVE_LONG_LONG)
+ unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo, t1;
+ lo = (ctx->sizeLo << 3);
+#else
+ unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f;
+ unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf);
+ PRUint64 lo = ctx->sizeLo;
+ PRUint32 t1;
+ lo.lo <<= 3;
+#endif
+
+ SHA512_Update(ctx, pad, padLen);
+
+#if defined(HAVE_LONG_LONG)
+ W[14] = 0;
+#else
+ W[14].lo = 0;
+ W[14].hi = 0;
+#endif
+
+ W[15] = lo;
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(W[15]);
+#endif
+ SHA512_Compress(ctx);
+
+ /* now output the answer */
+#if defined(IS_LITTLE_ENDIAN)
+ BYTESWAP8(H[0]);
+ BYTESWAP8(H[1]);
+ BYTESWAP8(H[2]);
+ BYTESWAP8(H[3]);
+ BYTESWAP8(H[4]);
+ BYTESWAP8(H[5]);
+ BYTESWAP8(H[6]);
+ BYTESWAP8(H[7]);
+#endif
+ padLen = PR_MIN(SHA512_LENGTH, maxDigestLen);
+ memcpy(digest, H, padLen);
+ if (digestLen)
+ *digestLen = padLen;
+}
+
+SECStatus
+SHA512_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA512_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH);
+
+ return SECSuccess;
+}
+
+
+SECStatus
+SHA512_Hash(unsigned char *dest, const char *src)
+{
+ return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+
+void SHA512_TraceState(SHA512Context *ctx) { }
+
+unsigned int
+SHA512_FlattenSize(SHA512Context *ctx)
+{
+ return sizeof *ctx;
+}
+
+SECStatus
+SHA512_Flatten(SHA512Context *ctx,unsigned char *space)
+{
+ PORT_Memcpy(space, ctx, sizeof *ctx);
+ return SECSuccess;
+}
+
+SHA512Context *
+SHA512_Resurrect(unsigned char *space, void *arg)
+{
+ SHA512Context *ctx = SHA512_NewContext();
+ if (ctx)
+ PORT_Memcpy(ctx, space, sizeof *ctx);
+ return ctx;
+}
+
+void SHA512_Clone(SHA512Context *dest, SHA512Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+
+/* ======================================================================= */
+/* SHA384 uses a SHA512Context as the real context.
+** The only differences between SHA384 an SHA512 are:
+** a) the intialization values for the context, and
+** b) the number of bytes of data produced as output.
+*/
+
+/* SHA-384 initial hash values */
+static const PRUint64 H384[8] = {
+#if PR_BYTES_PER_LONG == 8
+ 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL ,
+ 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL ,
+ 0x67332667ffc00b31UL , 0x8eb44a8768581511UL ,
+ 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL
+#else
+ ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507),
+ ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939),
+ ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511),
+ ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4)
+#endif
+};
+
+SHA384Context *
+SHA384_NewContext(void)
+{
+ return SHA512_NewContext();
+}
+
+void
+SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit)
+{
+ SHA512_DestroyContext(ctx, freeit);
+}
+
+void
+SHA384_Begin(SHA384Context *ctx)
+{
+ memset(ctx, 0, sizeof *ctx);
+ memcpy(H, H384, sizeof H384);
+}
+
+void
+SHA384_Update(SHA384Context *ctx, const unsigned char *input,
+ unsigned int inputLen)
+{
+ SHA512_Update(ctx, input, inputLen);
+}
+
+void
+SHA384_End(SHA384Context *ctx, unsigned char *digest,
+ unsigned int *digestLen, unsigned int maxDigestLen)
+{
+#define SHA_MIN(a,b) (a < b ? a : b)
+ unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH);
+ SHA512_End(ctx, digest, digestLen, maxLen);
+}
+
+SECStatus
+SHA384_HashBuf(unsigned char *dest, const unsigned char *src,
+ uint32 src_length)
+{
+ SHA512Context ctx;
+ unsigned int outLen;
+
+ SHA384_Begin(&ctx);
+ SHA512_Update(&ctx, src, src_length);
+ SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH);
+
+ return SECSuccess;
+}
+
+SECStatus
+SHA384_Hash(unsigned char *dest, const char *src)
+{
+ return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src));
+}
+
+void SHA384_TraceState(SHA384Context *ctx) { }
+
+unsigned int
+SHA384_FlattenSize(SHA384Context *ctx)
+{
+ return sizeof(SHA384Context);
+}
+
+SECStatus
+SHA384_Flatten(SHA384Context *ctx,unsigned char *space)
+{
+ return SHA512_Flatten(ctx, space);
+}
+
+SHA384Context *
+SHA384_Resurrect(unsigned char *space, void *arg)
+{
+ return SHA512_Resurrect(space, arg);
+}
+
+void SHA384_Clone(SHA384Context *dest, SHA384Context *src)
+{
+ memcpy(dest, src, sizeof *dest);
+}
+#endif /* Comment out unused code. */
+
+/* ======================================================================= */
+#ifdef SELFTEST
+#include <stdio.h>
+
+static const char abc[] = { "abc" };
+static const char abcdbc[] = {
+ "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+};
+static const char abcdef[] = {
+ "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
+ "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"
+};
+
+void
+dumpHash32(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 4) {
+ printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]);
+ }
+ printf("\n");
+}
+
+void test256(void)
+{
+ unsigned char outBuf[SHA256_LENGTH];
+
+ printf("SHA256, input = %s\n", abc);
+ SHA256_Hash(outBuf, abc);
+ dumpHash32(outBuf, sizeof outBuf);
+
+ printf("SHA256, input = %s\n", abcdbc);
+ SHA256_Hash(outBuf, abcdbc);
+ dumpHash32(outBuf, sizeof outBuf);
+}
+
+void
+dumpHash64(const unsigned char *buf, unsigned int bufLen)
+{
+ unsigned int i;
+ for (i = 0; i < bufLen; i += 8) {
+ if (i % 32 == 0)
+ printf("\n");
+ printf(" %02x%02x%02x%02x%02x%02x%02x%02x",
+ buf[i ], buf[i+1], buf[i+2], buf[i+3],
+ buf[i+4], buf[i+5], buf[i+6], buf[i+7]);
+ }
+ printf("\n");
+}
+
+void test512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ printf("SHA512, input = %s\n", abc);
+ SHA512_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA512, input = %s\n", abcdef);
+ SHA512_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+void time512(void)
+{
+ unsigned char outBuf[SHA512_LENGTH];
+
+ SHA512_Hash(outBuf, abc);
+ SHA512_Hash(outBuf, abcdef);
+}
+
+void test384(void)
+{
+ unsigned char outBuf[SHA384_LENGTH];
+
+ printf("SHA384, input = %s\n", abc);
+ SHA384_Hash(outBuf, abc);
+ dumpHash64(outBuf, sizeof outBuf);
+
+ printf("SHA384, input = %s\n", abcdef);
+ SHA384_Hash(outBuf, abcdef);
+ dumpHash64(outBuf, sizeof outBuf);
+}
+
+int main (int argc, char *argv[], char *envp[])
+{
+ int i = 1;
+ if (argc > 1) {
+ i = atoi(argv[1]);
+ }
+ if (i < 2) {
+ test256();
+ test512();
+ test384();
+ } else {
+ while (i-- > 0) {
+ time512();
+ }
+ printf("done\n");
+ }
+ return 0;
+}
+
+#endif
diff --git a/base/third_party/purify/pure.h b/base/third_party/purify/pure.h
new file mode 100644
index 0000000..56d4be6
--- /dev/null
+++ b/base/third_party/purify/pure.h
@@ -0,0 +1,145 @@
+/*
+ * Header file of Pure API function declarations.
+ *
+* (C) Copyright IBM Corporation. 2006, 2006. All Rights Reserved.
+ * You may recompile and redistribute these definitions as required.
+ *
+ * Version 1.0
+ */
+
+#ifdef PURIFY
+
+#if defined(c_plusplus) || defined(__cplusplus)
+extern "C" {
+#endif
+
+// Don't include this file directly, use purify.h instead.
+// If you need something that's not there, add it.
+#ifdef PURIFY_PRIVATE_INCLUDE
+
+#define PURE_H_VERSION 1
+#include <stddef.h>
+
+//////////////////////////////
+// API's Specific to Purify //
+//////////////////////////////
+
+// TRUE when Purify is running.
+int __cdecl PurifyIsRunning(void) ;
+//
+// Print a string to the viewer.
+//
+int __cdecl PurePrintf(const char *fmt, ...) ;
+int __cdecl PurifyPrintf(const char *fmt, ...) ;
+//
+// Purify functions for leak and memory-in-use functionalty.
+//
+size_t __cdecl PurifyNewInuse(void) ;
+size_t __cdecl PurifyAllInuse(void) ;
+size_t __cdecl PurifyClearInuse(void) ;
+size_t __cdecl PurifyNewLeaks(void) ;
+size_t __cdecl PurifyAllLeaks(void) ;
+size_t __cdecl PurifyClearLeaks(void) ;
+//
+// Purify functions for handle leakage.
+//
+size_t __cdecl PurifyAllHandlesInuse(void) ;
+size_t __cdecl PurifyNewHandlesInuse(void) ;
+//
+// Functions that tell you about the state of memory.
+//
+size_t __cdecl PurifyDescribe(void *addr) ;
+size_t __cdecl PurifyWhatColors(void *addr, size_t size) ;
+//
+// Functions to test the state of memory. If the memory is not
+// accessable, an error is signaled just as if there were a memory
+// reference and the function returns false.
+//
+int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) ; // size used to be an int, until IA64 came along
+int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) ;
+//
+// Functions to test the state of memory. If the memory is not
+// accessable, these functions return false. No error is signaled.
+//
+int __cdecl PurifyIsReadable(const void *addr, size_t size) ;
+int __cdecl PurifyIsWritable(const void *addr, size_t size) ;
+int __cdecl PurifyIsInitialized(const void *addr, size_t size) ;
+//
+// Functions to set the state of memory.
+//
+void __cdecl PurifyMarkAsInitialized(void *addr, size_t size) ;
+void __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) ;
+//
+// Functions to do late detection of ABWs, FMWs, IPWs.
+//
+#define PURIFY_HEAP_CRT (HANDLE) ~(__int64) 1 /* 0xfffffffe */
+#define PURIFY_HEAP_ALL (HANDLE) ~(__int64) 2 /* 0xfffffffd */
+#define PURIFY_HEAP_BLOCKS_LIVE 0x80000000
+#define PURIFY_HEAP_BLOCKS_DEFERRED_FREE 0x40000000
+#define PURIFY_HEAP_BLOCKS_ALL (PURIFY_HEAP_BLOCKS_LIVE|PURIFY_HEAP_BLOCKS_DEFERRED_FREE)
+int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) ;
+int __cdecl PurifySetLateDetectScanCounter(int counter);
+int __cdecl PurifySetLateDetectScanInterval(int seconds);
+//
+// Functions to support pool allocators
+//
+void __cdecl PurifySetPoolId(const void *mem, int id);
+int __cdecl PurifyGetPoolId(const void *mem);
+void __cdecl PurifySetUserData(const void *mem, void *data);
+void * __cdecl PurifyGetUserData(const void *mem);
+void __cdecl PurifyMapPool(int id, void(*fn)());
+
+
+////////////////////////////////
+// API's Specific to Quantify //
+////////////////////////////////
+
+// TRUE when Quantify is running.
+int __cdecl QuantifyIsRunning(void) ;
+
+//
+// Functions for controlling collection
+//
+int __cdecl QuantifyDisableRecordingData(void) ;
+int __cdecl QuantifyStartRecordingData(void) ;
+int __cdecl QuantifyStopRecordingData(void) ;
+int __cdecl QuantifyClearData(void) ;
+int __cdecl QuantifyIsRecordingData(void) ;
+
+// Add a comment to the dataset
+int __cdecl QuantifyAddAnnotation(char *) ;
+
+// Save the current data, creating a "checkpoint" dataset
+int __cdecl QuantifySaveData(void) ;
+
+// Set the name of the current thread in the viewer
+int __cdecl QuantifySetThreadName(char *) ;
+
+////////////////////////////////
+// API's Specific to Coverage //
+////////////////////////////////
+
+// TRUE when Coverage is running.
+int __cdecl CoverageIsRunning(void) ;
+//
+// Functions for controlling collection
+//
+int __cdecl CoverageDisableRecordingData(void) ;
+int __cdecl CoverageStartRecordingData(void) ;
+int __cdecl CoverageStopRecordingData(void) ;
+int __cdecl CoverageClearData(void) ;
+int __cdecl CoverageIsRecordingData(void) ;
+// Add a comment to the dataset
+int __cdecl CoverageAddAnnotation(char *) ;
+
+// Save the current data, creating a "checkpoint" dataset
+int __cdecl CoverageSaveData(void) ;
+
+
+#endif // PURIFY_PRIVATE_INCLUDE
+
+#if defined(c_plusplus) || defined(__cplusplus)
+}
+#endif
+
+#endif // PURIFY
diff --git a/base/third_party/purify/pure_api.c b/base/third_party/purify/pure_api.c
new file mode 100644
index 0000000..1248ac3
--- /dev/null
+++ b/base/third_party/purify/pure_api.c
@@ -0,0 +1,145 @@
+/*
+ * Header file of Pure API function declarations.
+ *
+ * Explicitly no copyright.
+ * You may recompile and redistribute these definitions as required.
+ *
+ * NOTE1: In some situations when compiling with MFC, you should
+ * enable the setting 'Not using precompiled headers' in Visual C++
+ * to avoid a compiler diagnostic.
+ *
+ * NOTE2: This file works through the use of deep magic. Calls to functions
+ * in this file are replaced with calls into the OCI runtime system
+ * when an instrumented version of this program is run.
+ *
+ * NOTE3: The static vars avoidGy_n (where n is a unique number) are used
+ * to prevent optimizing the functions away when compiler option
+ * /Gy is set. This is needed so that NOTE2 works properly.
+ */
+#ifdef PURIFY
+#pragma once
+ extern int errno;
+typedef int ptrdiff_t;
+typedef unsigned int size_t;
+typedef unsigned short wchar_t;
+static int avoidGy_1 = 0;
+static int avoidGy_2 = 0;
+static int avoidGy_3 = 0;
+static int avoidGy_4 = 0;
+static int avoidGy_5 = 0;
+static int avoidGy_6 = 0;
+static int avoidGy_7 = 0;
+static int avoidGy_8 = 0;
+static int avoidGy_9 = 0;
+static int avoidGy_10 = 0;
+static int avoidGy_11 = 0;
+static int avoidGy_12 = 0;
+static int avoidGy_13 = 0;
+static int avoidGy_14 = 0;
+static int avoidGy_15 = 0;
+static int avoidGy_16 = 0;
+static int avoidGy_17 = 0;
+static int avoidGy_18 = 0;
+static int avoidGy_19 = 0;
+static int avoidGy_20 = 0;
+static int avoidGy_21 = 0;
+static int avoidGy_22 = 0;
+static int avoidGy_23 = 0;
+static int avoidGy_24 = 0;
+static int avoidGy_25 = 0;
+static int avoidGy_26 = 0;
+static int avoidGy_27 = 0;
+static int avoidGy_28 = 0;
+static int avoidGy_29 = 0;
+static int avoidGy_30 = 0;
+static int avoidGy_31 = 0;
+static int avoidGy_32 = 0;
+static int avoidGy_33 = 0;
+static int avoidGy_34 = 0;
+static int avoidGy_35 = 0;
+static int avoidGy_36 = 0;
+static int avoidGy_37 = 0;
+static int avoidGy_38 = 0;
+static int avoidGy_39 = 0;
+static int avoidGy_40 = 0;
+static int avoidGy_41 = 0;
+static int avoidGy_42 = 0;
+static int avoidGy_43 = 0;
+static int avoidGy_44 = 0;
+static int avoidGy_45 = 0;
+static int avoidGy_46 = 0;
+static int avoidGy_47 = 0;
+static int avoidGy_48 = 0;
+static int avoidGy_49 = 0;
+static int avoidGy_50 = 0;
+static int avoidGy_51 = 0;
+static int avoidGy_52 = 0;
+static int avoidGy_53 = 0;
+static int avoidGy_54 = 0;
+static int avoidGy_55 = 0;
+static int avoidGy_56 = 0;
+static int avoidGy_57 = 0;
+static int avoidGy_58 = 0;
+static int avoidGy_59 = 0;
+static int avoidGy_60 = 0;
+static int avoidGy_61 = 0;
+static int avoidGy_62 = 0;
+static int avoidGy_63 = 0;
+static int avoidGy_64 = 0;
+static int avoidGy_65 = 0;
+static int avoidGy_PL_01 = 0;
+static int avoidGy_PL_02 = 0;
+__declspec(dllexport) int __cdecl PurePrintf(const char *fmt, ...) { if(!++avoidGy_1); fmt; return 0; }
+__declspec(dllexport) int __cdecl PurifyIsRunning(void) { if(!++avoidGy_2); return 0; }
+__declspec(dllexport) int __cdecl PurifyPrintf(const char *fmt, ...) { if(!++avoidGy_3); fmt; return 0; }
+__declspec(dllexport) size_t __cdecl PurifyNewInuse(void) { if(!++avoidGy_4); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyAllInuse(void) { if(!++avoidGy_5); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyClearInuse(void) { if(!++avoidGy_6); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyNewLeaks(void) { if(!++avoidGy_7); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyAllLeaks(void) { if(!++avoidGy_8); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyClearLeaks(void) { if(!++avoidGy_9); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyAllHandlesInuse(void) { if(!++avoidGy_10); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyNewHandlesInuse(void) { if(!++avoidGy_11); return 0; }
+__declspec(dllexport) size_t __cdecl PurifyDescribe(void *addr) { if(!++avoidGy_12); addr; return 0; }
+__declspec(dllexport) int __cdecl PurifyWhatColors(void *addr, size_t size) { if(!++avoidGy_13); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) { if(!++avoidGy_14); addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) { if(!++avoidGy_15); addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsReadable(const void *addr, size_t size) { if(!++avoidGy_16); addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsWritable(const void *addr, size_t size) { if(!++avoidGy_17); addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyIsInitialized(const void *addr, size_t size) { if(!++avoidGy_18); addr; size; return 1; }
+__declspec(dllexport) int __cdecl PurifyRed(void *addr, size_t size) { if(!++avoidGy_19); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyGreen(void *addr, size_t size) { if(!++avoidGy_20); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyYellow(void *addr, size_t size) { if(!++avoidGy_21); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyBlue(void *addr, size_t size) { if(!++avoidGy_22); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkAsInitialized(void *addr, size_t size) { if(!++avoidGy_23); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) { if(!++avoidGy_24); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkForTrap(void *addr, size_t size) { if(!++avoidGy_25); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyMarkForNoTrap(void *addr, size_t size) { if(!++avoidGy_26); addr; size; return 0; }
+__declspec(dllexport) int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr)
+ { if(!++avoidGy_27); hHeap; dwFlags; addr; return 1; }
+__declspec(dllexport) int __cdecl PurifySetLateDetectScanCounter(int counter) { if(!++avoidGy_28); counter; return 0; };
+__declspec(dllexport) int __cdecl PurifySetLateDetectScanInterval(int seconds) { if(!++avoidGy_29); seconds; return 0; };
+__declspec(dllexport) void __cdecl PurifySetPoolId(const void *mem, int id) { if(!++avoidGy_61); mem; id; return; };
+__declspec(dllexport) int __cdecl PurifyGetPoolId(const void *mem) { if(!++avoidGy_62); mem; return 0; };
+__declspec(dllexport) void __cdecl PurifySetUserData(const void *mem, void *data) { if(!++avoidGy_63); mem; data; return; };
+__declspec(dllexport) void * __cdecl PurifyGetUserData(const void *mem) { if(!++avoidGy_64); mem; return 0; };
+__declspec(dllexport) void __cdecl PurifyMapPool(int id, void(*fn)()) { if(!++avoidGy_65); id; fn; return; };
+__declspec(dllexport) int __cdecl CoverageIsRunning(void) { if(!++avoidGy_30); return 0; }
+__declspec(dllexport) int __cdecl CoverageDisableRecordingData(void) { if(!++avoidGy_31); return 0; }
+__declspec(dllexport) int __cdecl CoverageStartRecordingData(void) { if(!++avoidGy_32); return 0; }
+__declspec(dllexport) int __cdecl CoverageStopRecordingData(void) { if(!++avoidGy_33); return 0; }
+__declspec(dllexport) int __cdecl CoverageClearData(void) { if(!++avoidGy_34); return 0; }
+__declspec(dllexport) int __cdecl CoverageIsRecordingData(void) { if(!++avoidGy_35); return 0; }
+__declspec(dllexport) int __cdecl CoverageAddAnnotation(char *str) { if(!++avoidGy_36); str; return 0; }
+__declspec(dllexport) int __cdecl CoverageSaveData(void) { if(!++avoidGy_37); return 0; }
+__declspec(dllexport) int __cdecl QuantifyIsRunning(void) { if(!++avoidGy_42); return 0; }
+__declspec(dllexport) int __cdecl QuantifyDisableRecordingData(void) { if(!++avoidGy_43); return 0; }
+__declspec(dllexport) int __cdecl QuantifyStartRecordingData(void) { if(!++avoidGy_44); return 0; }
+__declspec(dllexport) int __cdecl QuantifyStopRecordingData(void) { if(!++avoidGy_45); return 0; }
+__declspec(dllexport) int __cdecl QuantifyClearData(void) { if(!++avoidGy_46); return 0; }
+__declspec(dllexport) int __cdecl QuantifyIsRecordingData(void) { if(!++avoidGy_47); return 0; }
+__declspec(dllexport) int __cdecl QuantifyAddAnnotation(char *str) { if(!++avoidGy_48); str; return 0; }
+__declspec(dllexport) int __cdecl QuantifySaveData(void) { if(!++avoidGy_49); return 0; }
+__declspec(dllexport) int __cdecl QuantifySetThreadName(const char *szName) { if(!++avoidGy_50) ; szName; return 0; }
+
+#endif // PURIFY
diff --git a/base/thread.cc b/base/thread.cc
new file mode 100644
index 0000000..6908b9f
--- /dev/null
+++ b/base/thread.cc
@@ -0,0 +1,258 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <windows.h>
+
+#include "base/thread.h"
+
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+
+// This class is used when starting a thread. It passes information to the
+// thread function. It is referenced counted so we can cleanup the event
+// object used to synchronize thread startup properly.
+class ThreadStartInfo : public base::RefCountedThreadSafe<ThreadStartInfo> {
+ public:
+ Thread* self;
+ HANDLE start_event;
+
+ explicit ThreadStartInfo(Thread* t)
+ : self(t),
+ start_event(CreateEvent(NULL, FALSE, FALSE, NULL)) {
+ }
+
+ ~ThreadStartInfo() {
+ CloseHandle(start_event);
+ }
+};
+
+// This task is used to trigger the message loop to exit.
+class ThreadQuitTask : public Task {
+ public:
+ virtual void Run() {
+ MessageLoop::current()->Quit();
+ Thread::SetThreadWasQuitProperly(true);
+ }
+};
+
+// Once an object is signaled, quits the current inner message loop.
+class QuitOnSignal : public MessageLoop::Watcher {
+ public:
+ explicit QuitOnSignal(HANDLE signal) : signal_(signal) {
+ }
+ virtual void OnObjectSignaled(HANDLE object) {
+ DCHECK_EQ(object, signal_);
+ MessageLoop::current()->WatchObject(signal_, NULL);
+ MessageLoop::current()->Quit();
+ }
+ private:
+ HANDLE signal_;
+ DISALLOW_EVIL_CONSTRUCTORS(QuitOnSignal);
+};
+
+Thread::Thread(const char *name)
+ : thread_(NULL),
+ thread_id_(0),
+ message_loop_(NULL),
+ name_(name) {
+ DCHECK(tls_index_) << "static initializer failed";
+}
+
+Thread::~Thread() {
+ Stop();
+}
+
+// We use this thread-local variable to record whether or not a thread exited
+// because its Stop method was called. This allows us to catch cases where
+// MessageLoop::Quit() is called directly, which is unexpected when using a
+// Thread to setup and run a MessageLoop.
+// Note that if we start doing complex stuff in other static initializers
+// this could cause problems.
+TLSSlot Thread::tls_index_ = ThreadLocalStorage::Alloc();
+
+void Thread::SetThreadWasQuitProperly(bool flag) {
+#ifndef NDEBUG
+ ThreadLocalStorage::Set(tls_index_, reinterpret_cast<void*>(flag));
+#endif
+}
+
+bool Thread::GetThreadWasQuitProperly() {
+ bool quit_properly = true;
+#ifndef NDEBUG
+ quit_properly = (ThreadLocalStorage::Get(tls_index_) != 0);
+#endif
+ return quit_properly;
+}
+
+// The information on how to set the thread name comes from
+// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
+#define MS_VC_EXCEPTION 0x406D1388
+
+typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+
+
+// On XP, you can only get the ThreadId of the current
+// thread. So it is expected that you'll call this after the
+// thread starts up; hence, it is static.
+void Thread::SetThreadName(const char* name, DWORD tid) {
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = tid;
+ info.dwFlags = 0;
+
+ __try {
+ RaiseException(MS_VC_EXCEPTION, 0,
+ sizeof(info)/sizeof(DWORD),
+ reinterpret_cast<DWORD*>(&info));
+ } __except(EXCEPTION_CONTINUE_EXECUTION) {
+ }
+}
+
+
+bool Thread::Start() {
+ return StartWithStackSize(0);
+}
+
+bool Thread::StartWithStackSize(size_t stack_size) {
+ DCHECK(!thread_id_ && !thread_);
+ SetThreadWasQuitProperly(false);
+ scoped_refptr<ThreadStartInfo> info = new ThreadStartInfo(this);
+
+ unsigned int flags = 0;
+ if (win_util::GetWinVersion() >= win_util::WINVERSION_XP && stack_size) {
+ flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
+ } else {
+ stack_size = 0;
+ }
+ thread_ = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL,
+ static_cast<unsigned int>(stack_size),
+ ThreadFunc,
+ info,
+ flags,
+ &thread_id_));
+ if (!thread_) {
+ DLOG(ERROR) << "failed to create thread";
+ return false;
+ }
+
+ // Wait for the thread to start and initialize message_loop_
+ WaitForSingleObject(info->start_event, INFINITE);
+ return true;
+}
+
+void Thread::Stop() {
+ InternalStop(false);
+}
+
+void Thread::NonBlockingStop() {
+ InternalStop(true);
+}
+
+void Thread::InternalStop(bool run_message_loop) {
+ if (!thread_)
+ return;
+
+ DCHECK_NE(thread_id_, GetCurrentThreadId()) << "Can't call Stop() on itself";
+
+ // We had better have a message loop at this point! If we do not, then it
+ // most likely means that the thread terminated unexpectedly, probably due
+ // to someone calling Quit() on our message loop directly.
+ DCHECK(message_loop_);
+
+ message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
+
+ if (run_message_loop) {
+ QuitOnSignal signal_watcher(thread_);
+ MessageLoop::current()->WatchObject(thread_, &signal_watcher);
+ bool old_state = MessageLoop::current()->NestableTasksAllowed();
+ MessageLoop::current()->SetNestableTasksAllowed(true);
+ MessageLoop::current()->Run();
+ // Restore task state.
+ MessageLoop::current()->SetNestableTasksAllowed(old_state);
+ } else {
+ // Wait for the thread to exit.
+ WaitForSingleObject(thread_, INFINITE);
+ }
+ CloseHandle(thread_);
+
+ // Reset state.
+ thread_ = NULL;
+ message_loop_ = NULL;
+}
+
+/*static*/
+unsigned __stdcall Thread::ThreadFunc(void* param) {
+ // The message loop for this thread.
+ MessageLoop message_loop;
+
+ Thread* self;
+
+ // Complete the initialization of our Thread object. We grab an owning
+ // reference to the ThreadStartInfo object to ensure that the start_event
+ // object is not prematurely closed.
+ {
+ scoped_refptr<ThreadStartInfo> info = static_cast<ThreadStartInfo*>(param);
+ self = info->self;
+ self->message_loop_ = &message_loop;
+ SetThreadName(self->thread_name().c_str(), GetCurrentThreadId());
+ message_loop.SetThreadName(self->thread_name());
+ SetEvent(info->start_event);
+ }
+
+ // Let the thread do extra initialization.
+ self->Init();
+
+ message_loop.Run();
+
+ // Let the thread do extra cleanup.
+ self->CleanUp();
+
+ // Assert that MessageLoop::Quit was called by ThreadQuitTask.
+ DCHECK(Thread::GetThreadWasQuitProperly());
+
+ // Clearing this here should be unnecessary since we should only be here if
+ // Thread::Stop was called (the above DCHECK asserts that). However, to help
+ // make it easier to track down problems in release builds, we null out the
+ // message_loop_ pointer.
+ self->message_loop_ = NULL;
+
+ // We can't receive messages anymore.
+ self->thread_id_ = 0;
+ return 0;
+}
diff --git a/base/thread.h b/base/thread.h
new file mode 100644
index 0000000..7ed2402
--- /dev/null
+++ b/base/thread.h
@@ -0,0 +1,135 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THREAD_H__
+#define BASE_THREAD_H__
+
+#include <wtypes.h>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/thread_local_storage.h"
+
+class MessageLoop;
+
+// A simple thread abstraction that establishes a MessageLoop on a new thread.
+// The consumer uses the MessageLoop of the thread to cause code to execute on
+// the thread. When this object is destroyed the thread is terminated. All
+// pending tasks queued on the thread's message loop will run to completion
+// before the thread is terminated.
+//
+class Thread {
+ public:
+ // Constructor.
+ // name is a display string to identify the thread.
+ explicit Thread(const char *name);
+
+ // Destroys the thread, stopping it if necessary.
+ //
+ virtual ~Thread();
+
+ // Starts the thread. Returns true if the thread was successfully started;
+ // otherwise, returns false. Upon successful return, the message_loop()
+ // getter will return non-null.
+ //
+ bool Start();
+
+ // Starts the thread. Behaves exactly like Start in addition to allow to
+ // override the default process stack size. This is not the initial stack size
+ // but the maximum stack size that thread is allowed to use.
+ bool StartWithStackSize(size_t stack_size);
+
+ // Signals the thread to exit and returns once the thread has exited. After
+ // this method returns, the Thread object is completely reset and may be used
+ // as if it were newly constructed (i.e., Start may be called again).
+ //
+ // Stop may be called multiple times and is simply ignored if the thread is
+ // already stopped.
+ //
+ // NOTE: This method is optional. It is not strictly necessary to call this
+ // method as the Thread's destructor will take care of stopping the thread if
+ // necessary.
+ //
+ void Stop();
+
+ // Signals the thread to exit and returns once the thread has exited.
+ // Meanwhile, a message loop is run. After this method returns, the Thread
+ // object is completely reset and may be used as if it were newly constructed
+ // (i.e., Start may be called again).
+ //
+ // NonBlockingStop may be called multiple times and is simply ignored if the
+ // thread is already stopped.
+ //
+ // NOTE: The behavior is the same as Stop() except that pending tasks are run.
+ void NonBlockingStop();
+
+ // Returns the message loop for this thread. Use the MessageLoop's
+ // Posttask methods to execute code on the thread. This only returns
+ // non-null after a successful call to Start. After Stop has been called,
+ // this will return NULL.
+ //
+ // NOTE: You must not call this MessageLoop's Quit method directly. Use
+ // the Thread's Stop method instead.
+ //
+ MessageLoop* message_loop() const { return message_loop_; }
+
+ // Set the name of this thread (for display in debugger too).
+ const std::string &thread_name() { return name_; }
+
+ static void SetThreadWasQuitProperly(bool flag);
+ static bool GetThreadWasQuitProperly();
+
+ // Sets the thread name if a debugger is currently attached. Has no effect
+ // otherwise. To set the name of the current thread, pass GetCurrentThreadId()
+ // as the tid parameter.
+ static void SetThreadName(const char* name, DWORD tid);
+
+ protected:
+ // Called just prior to starting the message loop
+ virtual void Init() { }
+
+ // Called just after the message loop ends
+ virtual void CleanUp() { }
+
+ // Stops the thread. Called by either Stop() or NonBlockingStop().
+ void InternalStop(bool run_message_loop);
+
+ private:
+ static unsigned __stdcall ThreadFunc(void* param);
+
+ HANDLE thread_;
+ unsigned thread_id_;
+ MessageLoop* message_loop_;
+ std::string name_;
+ static TLSSlot tls_index_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Thread);
+};
+
+#endif // BASE_THREAD_H__
diff --git a/base/thread_local_storage.h b/base/thread_local_storage.h
new file mode 100644
index 0000000..e27edb8
--- /dev/null
+++ b/base/thread_local_storage.h
@@ -0,0 +1,95 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_THREAD_LOCAL_STORAGE_H__
+#define BASE_THREAD_LOCAL_STORAGE_H__
+
+#include "base/basictypes.h"
+
+#ifdef WIN32
+typedef int TLSSlot;
+#else
+typedef pthread_key_t TLSSlot;
+#endif
+
+// Wrapper for thread local storage. This class doesn't
+// do much except provide an API for portability later.
+class ThreadLocalStorage {
+ public:
+ // Prototype for the TLS destructor function, which can be
+ // optionally used to cleanup thread local storage on
+ // thread exit. 'value' is the data that is stored
+ // in thread local storage.
+ typedef void (*TLSDestructorFunc)(void* value);
+
+ // Allocate a TLS 'slot'.
+ // 'destructor' is a pointer to a function to perform
+ // per-thread cleanup of this object. If set to NULL,
+ // no cleanup is done for this TLS slot.
+ // Returns an index > 0 on success, or -1 on failure.
+ static TLSSlot Alloc(TLSDestructorFunc destructor = NULL);
+
+ // Free a previously allocated TLS 'slot'.
+ // If a destructor was set for this slot, removes
+ // the destructor so that remaining threads exiting
+ // will not free data.
+ static void Free(TLSSlot slot);
+
+ // Get the thread-local value stored in slot 'slot'.
+ // Values are guaranteed to initially be zero.
+ static void* Get(TLSSlot slot);
+
+ // Set the thread-local value stored in slot 'slot' to
+ // value 'value'.
+ static void Set(TLSSlot slot, void* value);
+
+#ifdef WIN32
+ // Function called when on thread exit to call TLS
+ // destructor functions. This function is used internally.
+ static void ThreadExit();
+
+ private:
+ // Function to lazily initialize our thread local storage.
+ static void **Initialize();
+
+ private:
+ // The maximum number of 'slots' in our thread local storage stack.
+ // For now, this is fixed. We could either increase statically, or
+ // we could make it dynamic in the future.
+ static const int kThreadLocalStorageSize = 64;
+
+ static long tls_key_;
+ static long tls_max_;
+ static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize];
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadLocalStorage);
+};
+
+#endif // BASE_THREAD_LOCAL_STORAGE_H__
diff --git a/base/thread_local_storage_unittest.cc b/base/thread_local_storage_unittest.cc
new file mode 100644
index 0000000..fff0d2a
--- /dev/null
+++ b/base/thread_local_storage_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <process.h>
+
+#include "base/thread_local_storage.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// ignore warnings about ptr->int conversions that we use when
+// storing ints into ThreadLocalStorage.
+#pragma warning(disable : 4311 4312)
+
+namespace {
+ class ThreadLocalStorageTest : public testing::Test {
+ };
+}
+
+
+TEST(ThreadLocalStorageTest, Basics) {
+ int index = ThreadLocalStorage::Alloc();
+ ThreadLocalStorage::Set(index, reinterpret_cast<void*>(123));
+ int value = reinterpret_cast<int>(ThreadLocalStorage::Get(index));
+ EXPECT_EQ(value, 123);
+}
+
+const int kInitialTlsValue = 0x5555;
+static int tls_index = 0;
+
+unsigned __stdcall TLSTestThreadMain(void* param) {
+ // param contains the thread local storage index.
+ int *index = reinterpret_cast<int*>(param);
+ *index = kInitialTlsValue;
+
+ ThreadLocalStorage::Set(tls_index, index);
+
+ int *ptr = static_cast<int*>(ThreadLocalStorage::Get(tls_index));
+ EXPECT_EQ(ptr, index);
+ EXPECT_EQ(*ptr, kInitialTlsValue);
+ *index = 0;
+
+ ptr = static_cast<int*>(ThreadLocalStorage::Get(tls_index));
+ EXPECT_EQ(ptr, index);
+ EXPECT_EQ(*ptr, 0);
+ return 0;
+}
+
+void ThreadLocalStorageCleanup(void *value) {
+ int *ptr = reinterpret_cast<int*>(value);
+ if (ptr)
+ *ptr = kInitialTlsValue;
+}
+
+
+TEST(ThreadLocalStorageTest, TLSDestructors) {
+ // Create a TLS index with a destructor. Create a set of
+ // threads that set the TLS, while the destructor cleans it up.
+ // After the threads finish, verify that the value is cleaned up.
+ const int kNumThreads = 5;
+ HANDLE threads[kNumThreads];
+ int values[kNumThreads];
+
+ tls_index = ThreadLocalStorage::Alloc(ThreadLocalStorageCleanup);
+
+ // Spawn the threads.
+ for (int16 index = 0; index < kNumThreads; index++) {
+ values[index] = kInitialTlsValue;
+ void *argument = static_cast<void*>(&(values[index]));
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, TLSTestThreadMain, argument, 0, &thread_id));
+ EXPECT_NE(threads[index], (HANDLE)NULL);
+ }
+
+ // Wait for the threads to finish.
+ for (int index = 0; index < kNumThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], 60*1000);
+ EXPECT_EQ(rv, WAIT_OBJECT_0); // verify all threads finished
+
+ // verify that the destructor was called and that we reset.
+ EXPECT_EQ(values[index], kInitialTlsValue);
+ }
+}
diff --git a/base/thread_local_storage_win.cc b/base/thread_local_storage_win.cc
new file mode 100644
index 0000000..7fba9ef
--- /dev/null
+++ b/base/thread_local_storage_win.cc
@@ -0,0 +1,177 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "base/thread_local_storage.h"
+
+#include "base/logging.h"
+
+// In order to make TLS destructors work, we need to keep function
+// pointers to the destructor for each TLS that we allocate.
+// We make this work by allocating a single OS-level TLS, which
+// contains an array of slots for the application to use. In
+// parallel, we also allocate an array of destructors, which we
+// keep track of and call when threads terminate.
+
+// tls_key_ is the one native TLS that we use. It stores our
+// table.
+long ThreadLocalStorage::tls_key_ = TLS_OUT_OF_INDEXES;
+
+// tls_max_ is the high-water-mark of allocated thread local storage.
+// We intentionally skip 0 so that it is not confused with an
+// unallocated TLS slot.
+long ThreadLocalStorage::tls_max_ = 1;
+
+// An array of destructor function pointers for the slots. If
+// a slot has a destructor, it will be stored in its corresponding
+// entry in this array.
+ThreadLocalStorage::TLSDestructorFunc
+ ThreadLocalStorage::tls_destructors_[kThreadLocalStorageSize];
+
+void **ThreadLocalStorage::Initialize() {
+ if (tls_key_ == TLS_OUT_OF_INDEXES) {
+ long value = TlsAlloc();
+ DCHECK(value != TLS_OUT_OF_INDEXES);
+
+ // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES,
+ // go ahead and set it. Otherwise, do nothing, as another
+ // thread already did our dirty work.
+ if (InterlockedCompareExchange(&tls_key_, value, TLS_OUT_OF_INDEXES) !=
+ TLS_OUT_OF_INDEXES) {
+ // We've been shortcut. Another thread replaced tls_key_ first so we need
+ // to destroy our index and use the one the other thread got first.
+ TlsFree(value);
+ }
+ }
+ DCHECK(TlsGetValue(tls_key_) == NULL);
+
+ // Create an array to store our data.
+ void **tls_data = new void*[kThreadLocalStorageSize];
+ memset(tls_data, 0, sizeof(void*[kThreadLocalStorageSize]));
+ TlsSetValue(tls_key_, tls_data);
+ return tls_data;
+}
+
+TLSSlot ThreadLocalStorage::Alloc(TLSDestructorFunc destructor) {
+ if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_))
+ Initialize();
+
+ // Grab a new slot.
+ int slot = InterlockedIncrement(&tls_max_) - 1;
+ if (slot >= kThreadLocalStorageSize) {
+ NOTREACHED();
+ return -1;
+ }
+
+ // Setup our destructor.
+ tls_destructors_[slot] = destructor;
+ return slot;
+}
+
+void ThreadLocalStorage::Free(TLSSlot slot) {
+ // At this time, we don't reclaim old indices for TLS slots.
+ // So all we need to do is wipe the destructor.
+ tls_destructors_[slot] = NULL;
+}
+
+void* ThreadLocalStorage::Get(TLSSlot slot) {
+ void **tls_data = static_cast<void**>(TlsGetValue(tls_key_));
+ if (!tls_data)
+ tls_data = Initialize();
+ DCHECK(slot >= 0 && slot < kThreadLocalStorageSize);
+ return tls_data[slot];
+}
+
+void ThreadLocalStorage::Set(TLSSlot slot, void* value) {
+ void **tls_data = static_cast<void**>(TlsGetValue(tls_key_));
+ if (!tls_data)
+ tls_data = Initialize();
+ DCHECK(slot >= 0 && slot < kThreadLocalStorageSize);
+ tls_data[slot] = value;
+}
+
+void ThreadLocalStorage::ThreadExit() {
+ void **tls_data = static_cast<void**>(TlsGetValue(tls_key_));
+
+ // Maybe we have never initialized TLS for this thread.
+ if (!tls_data)
+ return;
+
+ for (int slot = 0; slot < tls_max_; slot++) {
+ if (tls_destructors_[slot] != NULL) {
+ void *value = tls_data[slot];
+ tls_destructors_[slot](value);
+ }
+ }
+
+ delete[] tls_data;
+
+ // In case there are other "onexit" handlers...
+ TlsSetValue(tls_key_, NULL);
+}
+
+// Thread Termination Callbacks.
+// Windows doesn't support a per-thread destructor with its
+// TLS primitives. So, we build it manually by inserting a
+// function to be called on each thread's exit.
+// This magic is from http://www.codeproject.com/threads/tls.asp
+// and it works for VC++ 7.0 and later.
+
+// This makes the linker create the TLS directory if it's not already
+// there. (e.g. if __declspec(thread) is not used).
+#pragma comment(linker, "/INCLUDE:__tls_used")
+
+// Static callback function to call with each thread termination.
+void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved)
+{
+ // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+
+ // and on W2K and W2K3. So don't assume it is sent.
+ if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason)
+ ThreadLocalStorage::ThreadExit();
+}
+
+// Note: .CRT section get merged with .rdata on x64 so it should be constant
+// data.
+//
+// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are
+// called automatically by the OS loader code (not the CRT) when the module is
+// loaded and on thread creation. They are NOT called if the module has been
+// loaded by a LoadLibrary() call. It must have implicitly been loaded at
+// process startup.
+// By implicitly loaded, I mean that it is directly referenced by the main EXE
+// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being
+// implicitly loaded.
+//
+// See VC\crt\src\tlssup.c for reference.
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK p_thread_callback = OnThreadExit;
+
+// Reset the default section.
+#pragma data_seg()
diff --git a/base/thread_unittest.cc b/base/thread_unittest.cc
new file mode 100644
index 0000000..ba91005
--- /dev/null
+++ b/base/thread_unittest.cc
@@ -0,0 +1,157 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class ThreadTest : public testing::Test {
+ };
+}
+
+class ToggleValue : public Task {
+ public:
+ explicit ToggleValue(bool* value) : value_(value) {
+ }
+ virtual void Run() {
+ *value_ = !*value_;
+ }
+ private:
+ bool* value_;
+};
+
+class SleepSome : public Task {
+ public:
+ explicit SleepSome(DWORD msec) : msec_(msec) {
+ }
+ virtual void Run() {
+ Sleep(msec_);
+ }
+ private:
+ DWORD msec_;
+};
+
+TEST(ThreadTest, BasicTest1) {
+ Thread a("BasicTest1");
+ a.Start();
+ EXPECT_TRUE(a.message_loop());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+ a.Start();
+ EXPECT_TRUE(a.message_loop());
+ a.Stop();
+ EXPECT_FALSE(a.message_loop());
+}
+
+TEST(ThreadTest, BasicTest2) {
+ Thread a("BasicTest2");
+ a.Start();
+ EXPECT_TRUE(a.message_loop());
+
+ bool was_invoked = false;
+ a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
+
+ // wait for the task to run (we could use a kernel event here
+ // instead to avoid busy waiting, but this is sufficient for
+ // testing purposes).
+ for (int i = 50; i >= 0 && !was_invoked; --i) {
+ Sleep(20);
+ }
+ EXPECT_TRUE(was_invoked);
+}
+
+TEST(ThreadTest, BasicTest3) {
+ bool was_invoked = false;
+ {
+ Thread a("BasicTest3");
+ a.Start();
+ EXPECT_TRUE(a.message_loop());
+
+ // Test that all events are dispatched before the Thread object is
+ // destroyed. We do this by dispatching a sleep event before the
+ // event that will toggle our sentinel value.
+ a.message_loop()->PostTask(FROM_HERE, new SleepSome(500));
+ a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
+ }
+ EXPECT_TRUE(was_invoked);
+}
+
+namespace {
+ class DummyTask : public Task {
+ public:
+ explicit DummyTask(int* counter) : counter_(counter) {
+ }
+ void Run() {
+ // Let's make sure that no other thread is running while we
+ // are executing this code. The sleeps help us assert that.
+ int initial_value = *counter_;
+ Sleep(1);
+ ++(*counter_);
+ Sleep(1);
+ int final_value = *counter_;
+ DCHECK(final_value == initial_value + 1);
+ }
+ private:
+ int* counter_;
+ };
+}
+
+namespace {
+ // This task just continuously posts events to its thread, keeping it well
+ // saturated with work. If our thread interlocking is not fair, then we will
+ // never exit.
+ class BusyTask : public Task {
+ public:
+ explicit BusyTask(HANDLE quit_event) : quit_event_(quit_event) {
+ }
+ void Run() {
+ if (WaitForSingleObject(quit_event_, 0) != WAIT_OBJECT_0)
+ MessageLoop::current()->PostTask(FROM_HERE, new BusyTask(quit_event_));
+ }
+ private:
+ HANDLE quit_event_;
+ };
+
+ // This task just tries to set the quit sentinel to signal the busy thread
+ // to stop doing work.
+ class InterruptBusyTask : public Task {
+ public:
+ explicit InterruptBusyTask(HANDLE quit_event) : quit_event_(quit_event) {
+ }
+ void Run() {
+ SetEvent(quit_event_);
+ }
+ private:
+ HANDLE quit_event_;
+ };
+}
+
diff --git a/base/time.cc b/base/time.cc
new file mode 100644
index 0000000..aa915e6
--- /dev/null
+++ b/base/time.cc
@@ -0,0 +1,190 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/time.h"
+#include "base/string_util.h"
+#include "base/third_party/nspr/prtime.h"
+
+#include "base/logging.h"
+
+namespace {
+
+// Time between resampling the un-granular clock for this API. 60 seconds.
+const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond;
+
+} // namespace
+
+// TimeDelta ------------------------------------------------------------------
+
+// static
+TimeDelta TimeDelta::FromDays(int64 days) {
+ return TimeDelta(days * Time::kMicrosecondsPerDay);
+}
+
+// static
+TimeDelta TimeDelta::FromHours(int64 hours) {
+ return TimeDelta(hours * Time::kMicrosecondsPerHour);
+}
+
+// static
+TimeDelta TimeDelta::FromMinutes(int64 minutes) {
+ return TimeDelta(minutes * Time::kMicrosecondsPerMinute);
+}
+
+// static
+TimeDelta TimeDelta::FromSeconds(int64 secs) {
+ return TimeDelta(secs * Time::kMicrosecondsPerSecond);
+}
+
+// static
+TimeDelta TimeDelta::FromMilliseconds(int64 ms) {
+ return TimeDelta(ms * Time::kMicrosecondsPerMillisecond);
+}
+
+// static
+TimeDelta TimeDelta::FromMicroseconds(int64 us) {
+ return TimeDelta(us);
+}
+
+int TimeDelta::InDays() const {
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
+}
+
+int TimeDelta::InHours() const {
+ return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
+}
+
+double TimeDelta::InSecondsF() const {
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
+}
+
+int64 TimeDelta::InSeconds() const {
+ return delta_ / Time::kMicrosecondsPerSecond;
+}
+
+double TimeDelta::InMillisecondsF() const {
+ return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMilliseconds() const {
+ return delta_ / Time::kMicrosecondsPerMillisecond;
+}
+
+int64 TimeDelta::InMicroseconds() const {
+ return delta_;
+}
+
+// Time -----------------------------------------------------------------------
+
+int64 Time::initial_time_ = 0;
+TimeTicks Time::initial_ticks_;
+
+// static
+void Time::InitializeClock()
+{
+ initial_ticks_ = TimeTicks::Now();
+ initial_time_ = CurrentWallclockMicroseconds();
+}
+
+// static
+Time Time::Now() {
+ if (initial_time_ == 0)
+ InitializeClock();
+
+ // We implement time using the high-resolution timers so that we can get
+ // timeouts which are smaller than 10-15ms. If we just used
+ // CurrentWallclockMicroseconds(), we'd have the less-granular timer.
+ //
+ // To make this work, we initialize the clock (initial_time) and the
+ // counter (initial_ctr). To compute the initial time, we can check
+ // the number of ticks that have elapsed, and compute the delta.
+ //
+ // To avoid any drift, we periodically resync the counters to the system
+ // clock.
+ while(true) {
+ TimeTicks ticks = TimeTicks::Now();
+
+ // Calculate the time elapsed since we started our timer
+ TimeDelta elapsed = ticks - initial_ticks_;
+
+ // Check if enough time has elapsed that we need to resync the clock.
+ if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) {
+ InitializeClock();
+ continue;
+ }
+
+ return elapsed + initial_time_;
+ }
+}
+
+// static
+Time Time::FromTimeT(time_t tt) {
+ if (tt == 0)
+ return Time(); // Preserve 0 so we can tell it doesn't exist.
+ return (tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset;
+}
+
+time_t Time::ToTimeT() const {
+ if (us_ == 0)
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond;
+}
+
+double Time::ToDoubleT() const {
+ if (us_ == 0)
+ return 0; // Preserve 0 so we can tell it doesn't exist.
+ return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
+ static_cast<double>(kMicrosecondsPerSecond));
+}
+
+Time Time::LocalMidnight() const {
+ Exploded exploded;
+ LocalExplode(&exploded);
+ exploded.hour = 0;
+ exploded.minute = 0;
+ exploded.second = 0;
+ exploded.millisecond = 0;
+ return FromLocalExploded(exploded);
+}
+
+// static
+bool Time::FromString(const wchar_t* time_string, Time* parsed_time) {
+ DCHECK((time_string != NULL) && (parsed_time != NULL));
+ std::string ascii_time_string = WideToUTF8(time_string);
+ if (ascii_time_string.length() == 0)
+ return false;
+ PRTime result_time = 0;
+ PRStatus result = PR_ParseTimeString(ascii_time_string.c_str(), PR_FALSE,
+ &result_time);
+ if (PR_SUCCESS != result)
+ return false;
+ result_time += kTimeTToMicrosecondsOffset;
+ *parsed_time = Time(result_time);
+ return true;
+}
diff --git a/base/time.h b/base/time.h
new file mode 100644
index 0000000..e0c806d
--- /dev/null
+++ b/base/time.h
@@ -0,0 +1,469 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Time represents an absolute point in time, internally represented as
+// microseconds (s/1,000,000) since a platform-dependent epoch. Each
+// platform's epoch, along with other system-dependent clock interface
+// routines, is defined in time_PLATFORM.cc.
+//
+// TimeDelta represents a duration of time, internally represented in
+// microseconds.
+//
+// TimeTicks represents an abstract time that is always incrementing for use
+// in measuring time durations. It is internally represented in microseconds.
+// It can not be converted to a human-readable time, but is guaranteed not to
+// decrease (if the user changes the computer clock, Time::Now() may actually
+// decrease or jump).
+//
+// These classes are represented as only a 64-bit value, so they can be
+// efficiently passed by value.
+
+#ifndef BASE_TIME_H__
+#define BASE_TIME_H__
+
+#ifdef WIN32
+// For FILETIME in FromFileTime, until it moves to a new converter class.
+// See TODO(iyengar) below.
+#include <windows.h>
+#endif
+
+#include <time.h>
+#include "base/basictypes.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
+
+class Time;
+class TimeTicks;
+
+// This unit test does a lot of manual time manipulation.
+class PageLoadTrackerUnitTest;
+
+// TimeDelta ------------------------------------------------------------------
+
+class TimeDelta {
+ public:
+ TimeDelta() : delta_(0) {
+ }
+
+ // Converts units of time to TimeDeltas.
+ static TimeDelta FromDays(int64 days);
+ static TimeDelta FromHours(int64 hours);
+ static TimeDelta FromMinutes(int64 minutes);
+ static TimeDelta FromSeconds(int64 secs);
+ static TimeDelta FromMilliseconds(int64 ms);
+ static TimeDelta FromMicroseconds(int64 us);
+
+ // Returns the internal numeric value of the TimeDelta object. Please don't
+ // use this and do arithmetic on it, as it is more error prone than using the
+ // provided operators.
+ int64 ToInternalValue() const {
+ return delta_;
+ }
+
+ // Returns the time delta in some unit. The F versions return a floating
+ // point value, the "regular" versions return a rounded-down value.
+ int InDays() const;
+ int InHours() const;
+ double InSecondsF() const;
+ int64 InSeconds() const;
+ double InMillisecondsF() const;
+ int64 InMilliseconds() const;
+ int64 InMicroseconds() const;
+
+ TimeDelta& operator=(TimeDelta other) {
+ delta_ = other.delta_;
+ return *this;
+ }
+
+ // Computations with other deltas.
+ TimeDelta operator+(TimeDelta other) const {
+ return TimeDelta(delta_ + other.delta_);
+ }
+ TimeDelta operator-(TimeDelta other) const {
+ return TimeDelta(delta_ - other.delta_);
+ }
+
+ TimeDelta& operator+=(TimeDelta other) {
+ delta_ += other.delta_;
+ return *this;
+ }
+ TimeDelta& operator-=(TimeDelta other) {
+ delta_ -= other.delta_;
+ return *this;
+ }
+ TimeDelta operator-() const {
+ return TimeDelta(-delta_);
+ }
+
+ // Computations with ints, note that we only allow multiplicative operations
+ // with ints, and additive operations with other deltas.
+ TimeDelta operator*(int64 a) const {
+ return TimeDelta(delta_ * a);
+ }
+ TimeDelta operator/(int64 a) const {
+ return TimeDelta(delta_ / a);
+ }
+ TimeDelta& operator*=(int64 a) {
+ delta_ *= a;
+ return *this;
+ }
+ TimeDelta& operator/=(int64 a) {
+ delta_ /= a;
+ return *this;
+ }
+ int64 operator/(TimeDelta a) const {
+ return delta_ / a.delta_;
+ }
+
+ // Defined below because it depends on the definition of the other classes.
+ Time operator+(Time t) const;
+ TimeTicks operator+(TimeTicks t) const;
+
+ // Comparison operators.
+ bool operator==(TimeDelta other) const {
+ return delta_ == other.delta_;
+ }
+ bool operator!=(TimeDelta other) const {
+ return delta_ != other.delta_;
+ }
+ bool operator<(TimeDelta other) const {
+ return delta_ < other.delta_;
+ }
+ bool operator<=(TimeDelta other) const {
+ return delta_ <= other.delta_;
+ }
+ bool operator>(TimeDelta other) const {
+ return delta_ > other.delta_;
+ }
+ bool operator>=(TimeDelta other) const {
+ return delta_ >= other.delta_;
+ }
+
+ private:
+ friend class Time;
+ friend class TimeTicks;
+ friend TimeDelta operator*(int64 a, TimeDelta td);
+
+ // Constructs a delta given the duration in microseconds. This is private
+ // to avoid confusion by callers with an integer constructor. Use
+ // FromSeconds, FromMilliseconds, etc. instead.
+ explicit TimeDelta(int64 delta_us) : delta_(delta_us) {
+ }
+
+ // Delta in microseconds.
+ int64 delta_;
+};
+
+inline TimeDelta operator*(int64 a, TimeDelta td) {
+ return TimeDelta(a * td.delta_);
+}
+
+// Time -----------------------------------------------------------------------
+
+// Represents a wall clock time.
+class Time {
+ public:
+ static const int64 kMillisecondsPerSecond = 1000;
+ static const int64 kMicrosecondsPerMillisecond = 1000;
+ static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond *
+ kMillisecondsPerSecond;
+ static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60;
+ static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60;
+ static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24;
+ static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7;
+
+ // Represents an exploded time that can be formatted nicely. This is kind of
+ // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few
+ // additions and changes to prevent errors.
+ struct Exploded {
+ int year; // Four digit year "2007"
+ int month; // 1-based month (values 1 = January, etc.)
+ int day_of_week; // 0-based day of week (0 = Sunday, etc.)
+ int day_of_month; // 1-based day of month (1-31)
+ int hour; // Hour within the current day (0-23)
+ int minute; // Minute within the current hour (0-59)
+ int second; // Second within the current minute (0-59 plus leap
+ // seconds which may take it up to 60).
+ int millisecond; // Milliseconds within the current second (0-999)
+ };
+
+ // Contains the NULL time. Use Time::Now() to get the current time.
+ explicit Time() : us_(0) {
+ }
+
+ // Returns true if the time object has not been initialized.
+ bool is_null() const {
+ return us_ == 0;
+ }
+
+ // Returns the current time. Watch out, the system might adjust its clock
+ // in which case time will actually go backwards. We don't guarantee that
+ // times are increasing, or that two calls to Now() won't be the same.
+ static Time Now();
+
+ // Converts to/from time_t in UTC and a Time class.
+ // TODO(brettw) this should be removed once everybody starts using the |Time|
+ // class.
+ static Time FromTimeT(time_t tt);
+ time_t ToTimeT() const;
+
+ // Converts time to a double which is the number of seconds since epoch
+ // (Jan 1, 1970). Webkit uses this format to represent time.
+ double ToDoubleT() const;
+
+#ifdef WIN32
+ static Time FromFileTime(FILETIME ft);
+ FILETIME ToFileTime() const;
+#endif
+
+ // Converts an exploded structure representing either the local time or UTC
+ // into a Time class.
+ static Time FromUTCExploded(const Exploded& exploded) {
+ return FromExploded(false, exploded);
+ }
+ static Time FromLocalExploded(const Exploded& exploded) {
+ return FromExploded(true, exploded);
+ }
+
+ // Converts an integer value representing Time to a class. This is used
+ // when deserializing a |Time| structure, using a value known to be
+ // compatible. It is not provided as a constructor because the integer type
+ // may be unclear from the perspective of a caller.
+ static Time FromInternalValue(int64 us) {
+ return Time(us);
+ }
+
+ // Converts a string representation of time to a Time object.
+ // An example of a time string which is converted is as below:-
+ // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified
+ // in the input string, we assume local time.
+ // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to
+ // a new time converter class.
+ static bool FromString(const wchar_t* time_string, Time* parsed_time);
+
+ // For serializing, use FromInternalValue to reconstitute. Please don't use
+ // this and do arithmetic on it, as it is more error prone than using the
+ // provided operators.
+ int64 ToInternalValue() const {
+ return us_;
+ }
+
+ // Fills the given exploded structure with either the local time or UTC from
+ // this time structure (containing UTC).
+ void UTCExplode(Exploded* exploded) const {
+ return Explode(false, exploded);
+ }
+ void LocalExplode(Exploded* exploded) const {
+ return Explode(true, exploded);
+ }
+
+ // Rounds this time down to the nearest day in local time. It will represent
+ // midnight on that day.
+ Time LocalMidnight() const;
+
+ Time& operator=(Time other) {
+ us_ = other.us_;
+ return *this;
+ }
+
+ // Compute the difference between two times.
+ TimeDelta operator-(Time other) const {
+ return TimeDelta(us_ - other.us_);
+ }
+
+ // Modify by some time delta.
+ Time& operator+=(TimeDelta delta) {
+ us_ += delta.delta_;
+ return *this;
+ }
+ Time& operator-=(TimeDelta delta) {
+ us_ -= delta.delta_;
+ return *this;
+ }
+
+ // Return a new time modified by some delta.
+ Time operator+(TimeDelta delta) const {
+ return us_ + delta.delta_;
+ }
+ Time operator-(TimeDelta delta) const {
+ return us_ - delta.delta_;
+ }
+
+ // Comparison operators
+ bool operator==(Time other) const {
+ return us_ == other.us_;
+ }
+ bool operator!=(Time other) const {
+ return us_ != other.us_;
+ }
+ bool operator<(Time other) const {
+ return us_ < other.us_;
+ }
+ bool operator<=(Time other) const {
+ return us_ <= other.us_;
+ }
+ bool operator>(Time other) const {
+ return us_ > other.us_;
+ }
+ bool operator>=(Time other) const {
+ return us_ >= other.us_;
+ }
+
+ private:
+ friend class TimeDelta;
+
+ // Platform-dependent wall clock interface
+ static int64 CurrentWallclockMicroseconds();
+
+ // Initialize or resynchronize the clock.
+ static void InitializeClock();
+
+ // Explodes the given time to either local time |is_local = true| or UTC
+ // |is_local = false|.
+ void Explode(bool is_local, Exploded* exploded) const;
+
+ // Unexplodes a given time assuming the source is either local time
+ // |is_local = true| or UTC |is_local = false|.
+ static Time FromExploded(bool is_local, const Exploded& exploded);
+
+ Time(int64 us) : us_(us) {
+ }
+
+ // The representation of Jan 1, 1970 UTC in microseconds since the
+ // platform-dependent epoch.
+ static const int64 kTimeTToMicrosecondsOffset;
+
+ // Time in microseconds in UTC.
+ int64 us_;
+
+ // The initial time sampled via this API.
+ static int64 initial_time_;
+
+ // The initial clock counter sampled via this API.
+ static TimeTicks initial_ticks_;
+};
+
+inline Time TimeDelta::operator+(Time t) const {
+ return Time(t.us_ + delta_);
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+class TimeTicks {
+ public:
+ TimeTicks() : ticks_(0) {
+ }
+
+ // Platform-dependent tick count representing "right now."
+ // The resolution of this clock is ~1-5ms. Resolution varies depending
+ // on hardware/operating system configuration.
+ static TimeTicks Now();
+
+ // Returns a platform-dependent high-resolution tick count. IT IS BROKEN ON
+ // SOME HARDWARE and is designed to be used for profiling and perf testing
+ // only (see the impl for more information).
+ static TimeTicks UnreliableHighResNow();
+
+
+ // Returns true if this object has not been initialized.
+ bool is_null() const {
+ return ticks_ == 0;
+ }
+
+ TimeTicks& operator=(TimeTicks other) {
+ ticks_ = other.ticks_;
+ return *this;
+ }
+
+ // Compute the difference between two times.
+ TimeDelta operator-(TimeTicks other) const {
+ return TimeDelta(ticks_ - other.ticks_);
+ }
+
+ // Modify by some time delta.
+ TimeTicks& operator+=(TimeDelta delta) {
+ ticks_ += delta.delta_;
+ return *this;
+ }
+ TimeTicks& operator-=(TimeDelta delta) {
+ ticks_ -= delta.delta_;
+ return *this;
+ }
+
+ // Return a new TimeTicks modified by some delta.
+ TimeTicks operator+(TimeDelta delta) const {
+ return TimeTicks(ticks_ + delta.delta_);
+ }
+ TimeTicks operator-(TimeDelta delta) const {
+ return TimeTicks(ticks_ - delta.delta_);
+ }
+
+ // Comparison operators
+ bool operator==(TimeTicks other) const {
+ return ticks_ == other.ticks_;
+ }
+ bool operator!=(TimeTicks other) const {
+ return ticks_ != other.ticks_;
+ }
+ bool operator<(TimeTicks other) const {
+ return ticks_ < other.ticks_;
+ }
+ bool operator<=(TimeTicks other) const {
+ return ticks_ <= other.ticks_;
+ }
+ bool operator>(TimeTicks other) const {
+ return ticks_ > other.ticks_;
+ }
+ bool operator>=(TimeTicks other) const {
+ return ticks_ >= other.ticks_;
+ }
+
+ protected:
+ friend class TimeDelta;
+ friend class PageLoadTrackerUnitTest;
+
+ // Please use Now() to create a new object. This is for internal use
+ // and testing. Ticks is in microseconds.
+ explicit TimeTicks(int64 ticks) : ticks_(ticks) {
+ }
+
+ // Tick count in microseconds.
+ int64 ticks_;
+
+#ifdef WIN32
+ // The function to use for counting ticks.
+ typedef int (__stdcall *TickFunction)(void);
+ static TickFunction tick_function_;
+#endif
+};
+
+inline TimeTicks TimeDelta::operator+(TimeTicks t) const {
+ return TimeTicks(t.ticks_ + delta_);
+}
+
+#endif // BASE_TIME_H__
diff --git a/base/time_mac.cc b/base/time_mac.cc
new file mode 100644
index 0000000..bd4be21
--- /dev/null
+++ b/base/time_mac.cc
@@ -0,0 +1,139 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/time.h"
+
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#include <time.h>
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+// The Time routines in this file use standard POSIX routines, or almost-
+// standard routines in the case of timegm.
+// The TimeTicks routines are Mach-specific.
+
+// Time -----------------------------------------------------------------------
+
+// The internal representation of Time uses time_t directly, so there is no
+// offset. The epoch is 1970-01-01 00:00:00 UTC.
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0);
+
+// static
+int64 Time::CurrentWallclockMicroseconds() {
+ struct timeval tv;
+ struct timezone tz = { 0, 0 }; // UTC
+ if (gettimeofday(&tv, &tz) != 0) {
+ DCHECK(0) << "Could not determine time of day";
+ }
+ // Combine seconds and microseconds in a 64-bit field containing microseconds
+ // since the epoch. That's enough for nearly 600 centuries.
+ return tv.tv_sec * GG_UINT64_C(1000000) + tv.tv_usec;
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded& exploded) {
+ struct tm timestruct;
+ timestruct.tm_sec = exploded.second;
+ timestruct.tm_min = exploded.minute;
+ timestruct.tm_hour = exploded.hour;
+ timestruct.tm_mday = exploded.day_of_month;
+ timestruct.tm_mon = exploded.month - 1;
+ timestruct.tm_year = exploded.year - 1900;
+ timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this
+ timestruct.tm_yday = 0; // mktime/timegm ignore this
+ timestruct.tm_isdst = -1; // attempt to figure it out
+ timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore
+ timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore
+
+ time_t seconds;
+ if (is_local)
+ seconds = mktime(&timestruct);
+ else
+ seconds = timegm(&timestruct);
+ DCHECK(seconds >= 0) << "mktime/timegm could not convert from exploded";
+
+ uint64 milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond;
+ return Time(milliseconds * kMicrosecondsPerMillisecond);
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ // Time stores times with microsecond resolution, but Exploded only carries
+ // millisecond resolution, so begin by being lossy.
+ uint64 milliseconds = us_ / kMicrosecondsPerMillisecond;
+ time_t seconds = milliseconds / kMillisecondsPerSecond;
+
+ struct tm timestruct;
+ if (is_local)
+ localtime_r(&seconds, &timestruct);
+ else
+ gmtime_r(&seconds, &timestruct);
+
+ exploded->year = timestruct.tm_year + 1900;
+ exploded->month = timestruct.tm_mon + 1;
+ exploded->day_of_week = timestruct.tm_wday;
+ exploded->day_of_month = timestruct.tm_mday;
+ exploded->hour = timestruct.tm_hour;
+ exploded->minute = timestruct.tm_min;
+ exploded->second = timestruct.tm_sec;
+ exploded->millisecond = milliseconds % kMillisecondsPerSecond;
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+// static
+TimeTicks TimeTicks::Now() {
+ static bool has_timebase_info = false;
+ static mach_timebase_info_data_t timebase_info;
+ if (!has_timebase_info) {
+ has_timebase_info = mach_timebase_info(&timebase_info) == KERN_SUCCESS;
+ }
+ DCHECK(has_timebase_info) << "Could not determine system tick rate";
+
+ // mach_absolute_time is it when it comes to ticks on the Mac. Other calls
+ // with less precision (such as TickCount) just call through to
+ // mach_absolute_time.
+
+ // timebase_info converts absolute time tick units into nanoseconds. Divide
+ // by 1000 to get microseconds up front to stave off overflows.
+ uint64_t absolute_micro = mach_absolute_time() / 1000 * timebase_info.numer /
+ timebase_info.denom;
+
+ // Don't bother with the rollover handling that the Windows version does.
+ // With numer and denom = 1 (the expected case), the 64-bit absolute time
+ // reported in nanoseconds is enough to last nearly 585 years.
+
+ return TimeTicks(absolute_micro);
+}
+
+// static
+TimeTicks TimeTicks::UnreliableHighResNow() {
+ return Now();
+}
diff --git a/base/time_unittest.cc b/base/time_unittest.cc
new file mode 100644
index 0000000..214ec50
--- /dev/null
+++ b/base/time_unittest.cc
@@ -0,0 +1,195 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <process.h>
+#include <time.h>
+
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Test conversions to/from time_t and exploding/unexploding.
+TEST(Time, TimeT) {
+ // C library time and exploded time.
+ time_t now_t_1 = time(NULL);
+ struct tm tms;
+ localtime_s(&tms, &now_t_1);
+
+ // Convert to ours.
+ Time our_time_1 = Time::FromTimeT(now_t_1);
+ Time::Exploded exploded;
+ our_time_1.LocalExplode(&exploded);
+
+ // This will test both our exploding and our time_t -> Time conversion.
+ EXPECT_EQ(tms.tm_year + 1900, exploded.year);
+ EXPECT_EQ(tms.tm_mon + 1, exploded.month);
+ EXPECT_EQ(tms.tm_mday, exploded.day_of_month);
+ EXPECT_EQ(tms.tm_hour, exploded.hour);
+ EXPECT_EQ(tms.tm_min, exploded.minute);
+ EXPECT_EQ(tms.tm_sec, exploded.second);
+
+ // Convert exploded back to the time struct.
+ Time our_time_2 = Time::FromLocalExploded(exploded);
+ EXPECT_TRUE(our_time_1 == our_time_2);
+
+ time_t now_t_2 = our_time_2.ToTimeT();
+ EXPECT_EQ(now_t_1, now_t_2);
+
+ // Conversions of 0 should stay 0.
+ EXPECT_EQ(0, Time().ToTimeT());
+ EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue());
+}
+
+TEST(Time, ZeroIsSymmetric) {
+ Time zeroTime(Time::FromTimeT(0));
+ EXPECT_EQ(0, zeroTime.ToTimeT());
+}
+
+TEST(Time, LocalExplode) {
+ Time a = Time::Now();
+ Time::Exploded exploded;
+ a.LocalExplode(&exploded);
+
+ Time b = Time::FromLocalExploded(exploded);
+
+ // The exploded structure doesn't have microseconds, so the result will be
+ // rounded to the nearest millisecond.
+ EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+}
+
+TEST(Time, UTCExplode) {
+ Time a = Time::Now();
+ Time::Exploded exploded;
+ a.UTCExplode(&exploded);
+
+ Time b = Time::FromUTCExploded(exploded);
+ EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1));
+}
+
+TEST(TimeTicks, Deltas) {
+ TimeTicks ticks_start = TimeTicks::Now();
+ Sleep(10);
+ TimeTicks ticks_stop = TimeTicks::Now();
+ TimeDelta delta = ticks_stop - ticks_start;
+ EXPECT_GE(delta.InMilliseconds(), 10);
+ EXPECT_GE(delta.InMicroseconds(), 10000);
+ EXPECT_EQ(delta.InSeconds(), 0);
+}
+
+namespace {
+
+class MockTimeTicks : public TimeTicks {
+ public:
+ static int Ticker() {
+ return static_cast<int>(InterlockedIncrement(&ticker_));
+ }
+
+ static void InstallTicker() {
+ old_tick_function_ = tick_function_;
+ tick_function_ = reinterpret_cast<TickFunction>(&Ticker);
+ ticker_ = -5;
+ }
+
+ static void UninstallTicker() {
+ tick_function_ = old_tick_function_;
+ }
+
+ private:
+ static volatile LONG ticker_;
+ static TickFunction old_tick_function_;
+};
+
+volatile LONG MockTimeTicks::ticker_;
+MockTimeTicks::TickFunction MockTimeTicks::old_tick_function_;
+
+HANDLE g_rollover_test_start;
+
+unsigned __stdcall RolloverTestThreadMain(void* param) {
+ int64 counter = reinterpret_cast<int64>(param);
+ DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+
+ TimeTicks last = TimeTicks::Now();
+ for (int index = 0; index < counter; index++) {
+ TimeTicks now = TimeTicks::Now();
+ int64 milliseconds = (now - last).InMilliseconds();
+ EXPECT_GT(milliseconds, 0);
+ EXPECT_LT(milliseconds, 250);
+ last = now;
+ }
+ return 0;
+}
+
+} // namespace
+
+TEST(TimeTicks, Rollover) {
+ // The internal counter rolls over at ~49days. We'll use a mock
+ // timer to test this case.
+ // Basic test algorithm:
+ // 1) Set clock to rollover - N
+ // 2) Create N threads
+ // 3) Start the threads
+ // 4) Each thread loops through TimeTicks() N times
+ // 5) Each thread verifies integrity of result.
+
+ const int kThreads = 8;
+ // Use int64 so we can cast into a void* without a compiler warning.
+ const int64 kChecks = 10;
+
+ // It takes a lot of iterations to reproduce the bug!
+ // (See bug 1081395)
+ for (int loop = 0; loop < 4096; loop++) {
+ // Setup
+ MockTimeTicks::InstallTicker();
+ g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0);
+ HANDLE threads[kThreads];
+
+ for (int index = 0; index < kThreads; index++) {
+ void* argument = reinterpret_cast<void*>(kChecks);
+ unsigned thread_id;
+ threads[index] = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0,
+ &thread_id));
+ EXPECT_NE((HANDLE)NULL, threads[index]);
+ }
+
+ // Start!
+ SetEvent(g_rollover_test_start);
+
+ // Wait for threads to finish
+ for (int index = 0; index < kThreads; index++) {
+ DWORD rv = WaitForSingleObject(threads[index], INFINITE);
+ EXPECT_EQ(rv, WAIT_OBJECT_0);
+ }
+
+ CloseHandle(g_rollover_test_start);
+
+ // Teardown
+ MockTimeTicks::UninstallTicker();
+ }
+}
diff --git a/base/time_win.cc b/base/time_win.cc
new file mode 100644
index 0000000..e7faa86
--- /dev/null
+++ b/base/time_win.cc
@@ -0,0 +1,244 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/time.h"
+
+#pragma comment(lib, "winmm.lib")
+#include <windows.h>
+#include <mmsystem.h>
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/logging.h"
+
+namespace {
+
+// From MSDN, FILETIME "Contains a 64-bit value representing the number of
+// 100-nanosecond intervals since January 1, 1601 (UTC)."
+int64 FileTimeToMicroseconds(const FILETIME& ft) {
+ // Need to bit_cast to fix alignment, then divide by 10 to convert
+ // 100-nanoseconds to milliseconds. This only works on little-endian
+ // machines.
+ return bit_cast<int64, FILETIME>(ft) / 10;
+}
+
+void MicrosecondsToFileTime(int64 us, FILETIME* ft) {
+ DCHECK(us >= 0) << "Time is less than 0, negative values are not "
+ "representable in FILETIME";
+
+ // Multiply by 10 to convert milliseconds to 100-nanoseconds. Bit_cast will
+ // handle alignment problems. This only works on little-endian machines.
+ *ft = bit_cast<FILETIME, int64>(us * 10);
+}
+
+} // namespace
+
+// Time -----------------------------------------------------------------------
+
+// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01
+// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the
+// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding
+// 1700, 1800, and 1900.
+// static
+const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000);
+
+// static
+int64 Time::CurrentWallclockMicroseconds() {
+ FILETIME ft;
+ ::GetSystemTimeAsFileTime(&ft);
+ return FileTimeToMicroseconds(ft);
+}
+
+// static
+Time Time::FromFileTime(FILETIME ft) {
+ return Time(FileTimeToMicroseconds(ft));
+}
+
+FILETIME Time::ToFileTime() const {
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+ return utc_ft;
+}
+
+// static
+Time Time::FromExploded(bool is_local, const Exploded& exploded) {
+ // Create the system struct representing our exploded time. It will either be
+ // in local time or UTC.
+ SYSTEMTIME st;
+ st.wYear = exploded.year;
+ st.wMonth = exploded.month;
+ st.wDayOfWeek = exploded.day_of_week;
+ st.wDay = exploded.day_of_month;
+ st.wHour = exploded.hour;
+ st.wMinute = exploded.minute;
+ st.wSecond = exploded.second;
+ st.wMilliseconds = exploded.millisecond;
+
+ // Convert to FILETIME.
+ FILETIME ft;
+ if (!SystemTimeToFileTime(&st, &ft)) {
+ NOTREACHED() << "Unable to convert time";
+ return Time(0);
+ }
+
+ // Ensure that it's in UTC.
+ if (is_local) {
+ FILETIME utc_ft;
+ LocalFileTimeToFileTime(&ft, &utc_ft);
+ return Time(FileTimeToMicroseconds(utc_ft));
+ }
+ return Time(FileTimeToMicroseconds(ft));
+}
+
+void Time::Explode(bool is_local, Exploded* exploded) const {
+ // FILETIME in UTC.
+ FILETIME utc_ft;
+ MicrosecondsToFileTime(us_, &utc_ft);
+
+ // FILETIME in local time if necessary.
+ BOOL success = TRUE;
+ FILETIME ft;
+ if (is_local)
+ success = FileTimeToLocalFileTime(&utc_ft, &ft);
+ else
+ ft = utc_ft;
+
+ // FILETIME in SYSTEMTIME (exploded).
+ SYSTEMTIME st;
+ if (!success || !FileTimeToSystemTime(&ft, &st)) {
+ NOTREACHED() << "Unable to convert time, don't know why";
+ ZeroMemory(exploded, sizeof(exploded));
+ return;
+ }
+
+ exploded->year = st.wYear;
+ exploded->month = st.wMonth;
+ exploded->day_of_week = st.wDayOfWeek;
+ exploded->day_of_month = st.wDay;
+ exploded->hour = st.wHour;
+ exploded->minute = st.wMinute;
+ exploded->second = st.wSecond;
+ exploded->millisecond = st.wMilliseconds;
+}
+
+// TimeTicks ------------------------------------------------------------------
+
+TimeTicks::TickFunction TimeTicks::tick_function_=
+ reinterpret_cast<TickFunction>(&timeGetTime);
+
+// static
+TimeTicks TimeTicks::Now() {
+ // Uses the multimedia timers on Windows to get a higher resolution clock.
+ // timeGetTime() provides a resolution which is variable depending on
+ // hardware and system configuration. It can also be changed by other
+ // apps. This class does not attempt to change the resolution of the
+ // timer, because we don't want to affect other applications.
+
+ // timeGetTime() should at least be accurate to ~5ms on all systems.
+ // timeGetTime() returns a 32-bit millisecond counter which has rollovers
+ // every ~49 days.
+ static DWORD last_tick_count = 0;
+ static int64 tick_rollover_accum = 0;
+ static Lock* tick_lock = NULL; // To protect during rollover periods.
+
+ // Lazily create the lock we use.
+ if (!tick_lock) {
+ Lock* new_lock = new Lock;
+ if (InterlockedCompareExchangePointer(
+ reinterpret_cast<PVOID*>(&tick_lock), new_lock, NULL)) {
+ delete new_lock;
+ }
+ }
+
+ // Atomically protect the low and high 32bit values for time.
+ // In the future we may be able to optimize with
+ // InterlockedCompareExchange64, but that doesn't work on XP.
+ DWORD tick_count;
+ int64 rollover_count;
+ {
+ AutoLock lock(*tick_lock);
+ tick_count = tick_function_();
+ if (tick_count < last_tick_count)
+ tick_rollover_accum += GG_INT64_C(0x100000000);
+
+ last_tick_count = tick_count;
+ rollover_count = tick_rollover_accum;
+ }
+
+ // GetTickCount returns milliseconds, we want microseconds.
+ return TimeTicks((tick_count + rollover_count) *
+ Time::kMicrosecondsPerMillisecond);
+}
+
+// Overview of time counters:
+// (1) CPU cycle counter. (Retrieved via RDTSC)
+// The CPU counter provides the highest resolution time stamp and is the least
+// expensive to retrieve. However, the CPU counter is unreliable and should not
+// be used in production. Its biggest issue is that it is per processor and it
+// is not synchronized between processors. Also, on some computers, the counters
+// will change frequency due to thermal and power changes, and stop in some
+// states.
+//
+// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high-
+// resolution (100 nanoseconds) time stamp but is comparatively more expensive
+// to retrieve. What QueryPerformanceCounter actually does is up to the HAL.
+// (with some help from ACPI).
+// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
+// in the worst case, it gets the counter from the rollover interrupt on the
+// programmable interrupt timer. In best cases, the HAL may conclude that the
+// RDTSC counter runs at a constant frequency, then it uses that instead. On
+// multiprocessor machines, it will try to verify the values returned from
+// RDTSC on each processor are consistent with each other, and apply a handful
+// of workarounds for known buggy hardware. In other words, QPC is supposed to
+// give consistent result on a multiprocessor computer, but it is unreliable in
+// reality due to bugs in BIOS or HAL on some, especially old computers.
+// With recent updates on HAL and newer BIOS, QPC is getting more reliable but
+// it should be used with caution.
+//
+// (3) System time. The system time provides a low-resolution (typically 10ms
+// to 55 milliseconds) time stamp but is comparatively less expensive to
+// retrieve and more reliable.
+
+// static
+TimeTicks TimeTicks::UnreliableHighResNow() {
+ // Cached clock frequency -> microseconds. This assumes that the clock
+ // frequency is faster than one microsecond (which is 1MHz, should be OK).
+ static int64 ticks_per_microsecond = 0;
+
+ if (ticks_per_microsecond == 0) {
+ LARGE_INTEGER ticks_per_sec = { 0, 0 };
+ if (!QueryPerformanceFrequency(&ticks_per_sec))
+ return TimeTicks(0); // Broken, we don't guarantee this function works.
+ ticks_per_microsecond =
+ ticks_per_sec.QuadPart / Time::kMicrosecondsPerSecond;
+ }
+
+ LARGE_INTEGER now;
+ QueryPerformanceCounter(&now);
+ return TimeTicks(now.QuadPart / ticks_per_microsecond);
+}
diff --git a/base/timer.cc b/base/timer.cc
new file mode 100644
index 0000000..4db8b88
--- /dev/null
+++ b/base/timer.cc
@@ -0,0 +1,346 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/timer.h"
+#include <mmsystem.h>
+
+#include "base/atomic.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+
+// Note about hi-resolution timers.
+// This class would *like* to provide high resolution timers. Windows timers
+// using SetTimer() have a 10ms granularity. We have to use WM_TIMER as a
+// wakeup mechanism because the application can enter modal windows loops where
+// it is not running our MessageLoop; the only way to have our timers fire in
+// these cases is to post messages there.
+//
+// To provide sub-10ms timers, we process timers directly from our main
+// MessageLoop. For the common case, timers will be processed there as the
+// message loop does its normal work. However, we *also* set the system timer
+// so that WM_TIMER events fire. This mops up the case of timers not being
+// able to work in modal message loops. It is possible for the SetTimer to
+// pop and have no pending timers, because they could have already been
+// processed by the message loop itself.
+//
+// We use a single SetTimer corresponding to the timer that will expire
+// soonest. As new timers are created and destroyed, we update SetTimer.
+// Getting a spurrious SetTimer event firing is benign, as we'll just be
+// processing an empty timer queue.
+
+static const wchar_t kWndClass[] = L"Chrome_TimerMessageWindow";
+
+static LRESULT CALLBACK MessageWndProc(HWND hwnd,
+ UINT message,
+ WPARAM wparam,
+ LPARAM lparam) {
+ if (message == WM_TIMER) {
+ // Timer not firing? Maybe you're suffering from a WM_PAINTstorm. Make sure
+ // any WM_PAINT handler you have calls BeginPaint and EndPaint to validate
+ // the invalid region, otherwise you will be flooded with paint messages
+ // that trump WM_TIMER when PeekMessage is called.
+ UINT_PTR timer_id = static_cast<UINT_PTR>(wparam);
+ TimerManager* tm = reinterpret_cast<TimerManager*>(timer_id);
+ return tm->MessageWndProc(hwnd, message, wparam, lparam);
+ }
+
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+// static
+int32 Timer::timer_id_counter_ = 0;
+
+//-----------------------------------------------------------------------------
+// Timer
+
+Timer::Timer(int delay, Task* task, bool repeating)
+ : delay_(delay),
+ task_(task),
+ repeating_(repeating) {
+ timer_id_ = base::AtomicIncrement(&timer_id_counter_);
+ DCHECK(delay >= 0);
+ Reset();
+}
+
+void Timer::Reset() {
+ creation_time_ = Time::Now();
+ fire_time_ = creation_time_ + TimeDelta::FromMilliseconds(delay_);
+ DHISTOGRAM_COUNTS(L"Timer.Durations", delay_);
+}
+
+//-----------------------------------------------------------------------------
+// TimerPQueue
+
+void TimerPQueue::RemoveTimer(Timer* timer) {
+ const std::vector<Timer*>::iterator location =
+ find(c.begin(), c.end(), timer);
+ if (location != c.end()) {
+ c.erase(location);
+ make_heap(c.begin(), c.end(), TimerComparison());
+ }
+}
+
+bool TimerPQueue::ContainsTimer(const Timer* timer) const {
+ return find(c.begin(), c.end(), timer) != c.end();
+}
+
+//-----------------------------------------------------------------------------
+// TimerManager
+
+TimerManager::TimerManager()
+ : use_broken_delay_(false),
+ message_hwnd_(NULL),
+ use_native_timers_(true),
+ message_loop_(NULL) {
+ // We've experimented with all sorts of timers, and initially tried
+ // to avoid using timeBeginPeriod because it does affect the system
+ // globally. However, after much investigation, it turns out that all
+ // of the major plugins (flash, windows media 9-11, and quicktime)
+ // already use timeBeginPeriod to increase the speed of the clock.
+ // Since the browser must work with these plugins, the browser already
+ // needs to support a fast clock. We may as well use this ourselves,
+ // as it really is the best timer mechanism for our needs.
+ timeBeginPeriod(1);
+
+ // Initialize the Message HWND in the constructor so that the window
+ // belongs to the same thread as the message loop (this is important!)
+ GetMessageHWND();
+}
+
+TimerManager::~TimerManager() {
+ // Match timeBeginPeriod() from construction.
+ timeEndPeriod(1);
+
+ if (message_hwnd_ != NULL)
+ DestroyWindow(message_hwnd_);
+
+ // Be nice to unit tests, and discard and delete all timers along with the
+ // embedded task objects by handing off to MessageLoop (which would have Run()
+ // and optionally deleted the objects).
+ while (timers_.size()) {
+ Timer* pending = timers_.top();
+ timers_.pop();
+ message_loop_->DiscardTimer(pending);
+ }
+}
+
+
+Timer* TimerManager::StartTimer(int delay, Task* task, bool repeating) {
+ Timer* t = new Timer(delay, task, repeating);
+ StartTimer(t);
+ return t;
+}
+
+void TimerManager::StopTimer(Timer* timer) {
+ // Make sure the timer is actually running.
+ if (!IsTimerRunning(timer))
+ return;
+ // Kill the active timer, and remove the pending entry from the queue.
+ if (timer != timers_.top()) {
+ timers_.RemoveTimer(timer);
+ } else {
+ timers_.pop();
+ UpdateWindowsWmTimer(); // We took away the head of our queue.
+ }
+}
+
+void TimerManager::ResetTimer(Timer* timer) {
+ StopTimer(timer);
+ timer->Reset();
+ StartTimer(timer);
+}
+
+bool TimerManager::IsTimerRunning(const Timer* timer) const {
+ return timers_.ContainsTimer(timer);
+}
+
+Timer* TimerManager::PeekTopTimer() {
+ if (timers_.empty())
+ return NULL;
+ return timers_.top();
+}
+
+bool TimerManager::RunSomePendingTimers() {
+ bool did_work = false;
+ bool allowed_to_run = message_loop()->NestableTasksAllowed();
+ // Process a small group of timers. Cap the maximum number of timers we can
+ // process so we don't deny cycles to other parts of the process when lots of
+ // timers have been set.
+ const int kMaxTimersPerCall = 2;
+ for (int i = 0; i < kMaxTimersPerCall; ++i) {
+ if (timers_.empty() || GetCurrentDelay() > 0)
+ break;
+
+ // Get a pending timer. Deal with updating the timers_ queue and setting
+ // the TopTimer. We'll execute the timer task only after the timer queue
+ // is back in a consistent state.
+ Timer* pending = timers_.top();
+ // If pending task isn't invoked_later, then it must be possible to run it
+ // now (i.e., current task needs to be reentrant).
+ // TODO(jar): We may block tasks that we can queue from being popped.
+ if (!message_loop()->NestableTasksAllowed() &&
+ !pending->task()->is_owned_by_message_loop())
+ break;
+
+ timers_.pop();
+ did_work = true;
+
+ // If the timer is repeating, add it back to the list of timers to process.
+ if (pending->repeating()) {
+ pending->Reset();
+ timers_.push(pending);
+ }
+
+ message_loop()->RunTimerTask(pending);
+ }
+
+ // Restart the WM_TIMER (if necessary).
+ if (did_work)
+ UpdateWindowsWmTimer();
+
+ return did_work;
+}
+
+// Note: Caller is required to call timer->Reset() before calling StartTimer().
+// TODO(jar): change API so that Reset() is called as part of StartTimer, making
+// the API a little less error prone.
+void TimerManager::StartTimer(Timer* timer) {
+ // Make sure the timer is not running.
+ if (IsTimerRunning(timer))
+ return;
+
+ timers_.push(timer); // Priority queue will sort the timer into place.
+
+ if (timers_.top() == timer)
+ UpdateWindowsWmTimer(); // We are new head of queue.
+}
+
+void TimerManager::UpdateWindowsWmTimer() {
+ if (!use_native_timers_)
+ return;
+
+ if (timers_.empty()) {
+ KillTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this));
+ return;
+ }
+
+ int delay = GetCurrentDelay();
+ if (delay < USER_TIMER_MINIMUM)
+ delay = USER_TIMER_MINIMUM;
+
+ // Simulates malfunctioning, early firing timers. Pending tasks should
+ // only be invoked when the delay they specify has elapsed.
+ if (use_broken_delay_)
+ delay = 10;
+
+ // Create a WM_TIMER event that will wake us up to check for any pending
+ // timers (in case the message loop was otherwise starving us).
+ SetTimer(GetMessageHWND(), reinterpret_cast<UINT_PTR>(this), delay, NULL);
+}
+
+int TimerManager::GetCurrentDelay() {
+ if (timers_.empty())
+ return -1;
+ int delay = timers_.top()->current_delay();
+ if (delay < 0)
+ delay = 0;
+ return delay;
+}
+
+int TimerManager::MessageWndProc(HWND hwnd, UINT message, WPARAM wparam,
+ LPARAM lparam) {
+ DCHECK(!lparam);
+ DCHECK(this == message_loop()->timer_manager());
+ if (message_loop()->NestableTasksAllowed())
+ RunSomePendingTimers();
+ else
+ UpdateWindowsWmTimer();
+ return 0;
+}
+
+MessageLoop* TimerManager::message_loop() {
+ if (!message_loop_)
+ message_loop_ = MessageLoop::current();
+ DCHECK(message_loop_ == MessageLoop::current());
+ return message_loop_;
+}
+
+
+HWND TimerManager::GetMessageHWND() {
+ if (!message_hwnd_) {
+ HINSTANCE hinst = GetModuleHandle(NULL);
+
+ WNDCLASSEX wc = {0};
+ wc.cbSize = sizeof(wc);
+ wc.lpfnWndProc = ::MessageWndProc;
+ wc.hInstance = hinst;
+ wc.lpszClassName = kWndClass;
+ RegisterClassEx(&wc);
+
+ message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0,
+ hinst, 0);
+ DCHECK(message_hwnd_);
+ }
+ return message_hwnd_;
+}
+
+//-----------------------------------------------------------------------------
+// SimpleTimer
+
+SimpleTimer::SimpleTimer(TimeDelta delay, Task* task, bool repeating)
+ : timer_(static_cast<int>(delay.InMilliseconds()), task, repeating),
+ owns_task_(true) {
+}
+
+SimpleTimer::~SimpleTimer() {
+ Stop();
+
+ if (owns_task_)
+ delete timer_.task();
+}
+
+void SimpleTimer::Start() {
+ DCHECK(timer_.task());
+ timer_.Reset();
+ MessageLoop::current()->timer_manager()->StartTimer(&timer_);
+}
+
+void SimpleTimer::Stop() {
+ MessageLoop::current()->timer_manager()->StopTimer(&timer_);
+}
+
+bool SimpleTimer::IsRunning() const {
+ return MessageLoop::current()->timer_manager()->IsTimerRunning(&timer_);
+}
+
+void SimpleTimer::Reset() {
+ DCHECK(timer_.task());
+ MessageLoop::current()->timer_manager()->ResetTimer(&timer_);
+}
diff --git a/base/timer.h b/base/timer.h
new file mode 100644
index 0000000..39eb313
--- /dev/null
+++ b/base/timer.h
@@ -0,0 +1,320 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TIMER_H_
+#define BASE_TIMER_H_
+
+#include <math.h>
+#include <windows.h>
+
+#include <queue>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/time.h"
+
+// Timer/TimerManager are objects designed to help setting timers.
+// Goals of TimerManager:
+// - have only one Windows system timer for all app timer functionality
+// - work around bugs with timers firing arbitrarily earlier than specified
+// - provide the ability to run timers even if the application is in a
+// windows modal app loop.
+
+class TimerManager;
+class Task;
+class MessageLoop;
+
+class Timer {
+ public:
+ Timer(int delay, Task* task, bool repeating);
+
+ Task* task() const { return task_; }
+ void set_task(Task* task) { task_ = task; }
+
+ int current_delay() const {
+ // Be careful here. Timers have a precision of microseconds,
+ // but this API is in milliseconds. If there are 5.5ms left,
+ // should the delay be 5 or 6? It should be 6 to avoid timers
+ // firing early. Implement ceiling by adding 999us prior to
+ // conversion to ms.
+ double delay = ceil((fire_time_ - Time::Now()).InMillisecondsF());
+ return static_cast<int>(delay);
+ }
+
+ const Time &fire_time() const { return fire_time_; }
+
+ bool repeating() const { return repeating_; }
+
+ // Update (or fill in) creation_time_, and calculate future fire_time_ based
+ // on current time plus delay_.
+ void Reset();
+
+ int id() const { return timer_id_; }
+
+ protected:
+ // Protected (rather than private) so that we can access from unit tests.
+
+ // The time when the timer should fire.
+ Time fire_time_;
+
+ private:
+ // A sequence number for all allocated times (used to break ties when
+ // comparing times in the TimerManager, and assure FIFO execution sequence).
+ static int32 timer_id_counter_;
+
+ // The task that is run when this timer fires.
+ Task* task_;
+
+ // Timer delay in milliseconds.
+ int delay_;
+
+ // A monotonically increasing timer id. Used
+ // for ordering two timers which have the same
+ // timestamp in a FIFO manner.
+ int timer_id_;
+
+ // Whether or not this timer repeats.
+ const bool repeating_;
+
+ // The tick count when the timer was "created". (i.e. when its current
+ // iteration started.)
+ Time creation_time_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Timer);
+};
+
+class TimerComparison {
+ public:
+ bool operator() (const Timer* t1, const Timer* t2) const {
+ const Time& f1 = t1->fire_time();
+ const Time& f2 = t2->fire_time();
+ // If the two timers have the same delay, revert to using
+ // the timer_id to maintain FIFO ordering.
+ if (f1 == f2) {
+ // Gracefully handle wrap as we try to return (t1->id() > t2->id());
+ int delta = t1->id() - t2->id();
+ // Assuming the delta is smaller than 2**31, we'll always get the right
+ // answer (in terms of sign of delta).
+ return delta > 0;
+ }
+ return f1 > f2;
+ }
+};
+
+// Subclass priority_queue to provide convenient access to removal from this
+// list.
+//
+// Terminology: The "pending" timer is the timer at the top of the queue,
+// i.e. the timer whose task needs to be Run next.
+class TimerPQueue : public std::priority_queue<Timer*,
+ std::vector<Timer*>,
+ TimerComparison> {
+ public:
+ // Removes |timer| from the queue.
+ void RemoveTimer(Timer* timer);
+
+ // Returns true if the queue contains |timer|.
+ bool ContainsTimer(const Timer* timer) const;
+};
+
+// There is one TimerManager per thread, owned by the MessageLoop.
+// Timers can either be fired directly by the MessageLoop, or by
+// SetTimer and a WM_TIMER message. The advantage of the former
+// is that we can make timers fire significantly faster than the 10ms
+// granularity provided by SetTimer(). The advantage of SetTimer()
+// is that modal message loops which don't run our MessageLoop
+// code will still be able to process WM_TIMER messages.
+//
+// Note: TimerManager is not thread safe. You cannot set timers
+// onto a thread other than your own.
+class TimerManager {
+ public:
+ TimerManager();
+ ~TimerManager();
+
+ // Create and start a new timer. |task| is owned by the caller, as is the
+ // timer object that is returned.
+ Timer* StartTimer(int delay, Task* task, bool repeating);
+
+ // Flag indicating whether the timer manager should use the OS
+ // timers or not. Default is true. MessageLoops which are not reliably
+ // called due to nested windows message loops should set this to
+ // true.
+ bool use_native_timers() { return use_native_timers_; }
+ void set_use_native_timers(bool value) { use_native_timers_ = value; }
+
+ // Starts a timer. This is a no-op if the timer is already started.
+ void StartTimer(Timer* timer);
+
+ // Stop a timer. This is a no-op if the timer is already stopped.
+ void StopTimer(Timer* timer);
+
+ // Reset an existing timer, which may or may not be currently in the queue of
+ // upcoming timers. The timer's parameters are unchanged; it simply begins
+ // counting down again as if it was just created.
+ void ResetTimer(Timer* timer);
+
+ // Returns true if |timer| is in the queue of upcoming timers.
+ bool IsTimerRunning(const Timer* timer) const;
+
+ // Run some small number of timers.
+ // Returns true if it runs a task, false otherwise.
+ bool RunSomePendingTimers();
+
+ // The number of milliseconds remaining until the pending timer (top of the
+ // pqueue) needs to be fired. Returns -1 if no timers are pending.
+ int GetCurrentDelay();
+
+ // A handler for WM_TIMER messages.
+ // If a task is not running currently, it runs some timer tasks (if there are
+ // some ready to fire), otherwise it just updates the WM_TIMER to be called
+ // again (hopefully when it is allowed to run a task).
+ int MessageWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
+
+ // Return cached copy of MessageLoop::current().
+ MessageLoop* message_loop();
+
+#ifdef UNIT_TEST
+ // For testing only, used to simulate broken early-firing WM_TIMER
+ // notifications by setting arbitrarily small delays in SetTimer.
+ void set_use_broken_delay(bool use_broken_delay) {
+ use_broken_delay_ = use_broken_delay;
+ }
+#endif
+
+ protected:
+ // Peek at the timer which will fire soonest.
+ Timer* PeekTopTimer();
+
+ private:
+ // Update our Windows WM_TIMER to match our most immediately pending timer.
+ void UpdateWindowsWmTimer();
+
+ // Retrieve the Message Window that handles WM_TIMER messages from the
+ // system.
+ HWND GetMessageHWND();
+
+ TimerPQueue timers_;
+
+ bool use_broken_delay_;
+
+ HWND message_hwnd_;
+
+ // Flag to enable/disable use of native timers.
+ bool use_native_timers_;
+
+ // A lazily cached copy of MessageLoop::current.
+ MessageLoop* message_loop_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TimerManager);
+};
+
+// A simple wrapper for the Timer / TimerManager API. This is a helper class.
+// Use OneShotTimer or RepeatingTimer instead.
+class SimpleTimer {
+ public:
+ // Stops the timer.
+ ~SimpleTimer();
+
+ // Call this method to explicitly start the timer. This is a no-op if the
+ // timer is already running.
+ void Start();
+
+ // Call this method to explicitly stop the timer. This is a no-op if the
+ // timer is not running.
+ void Stop();
+
+ // Returns true if the timer is running (i.e., not stopped).
+ bool IsRunning() const;
+
+ // Short-hand for calling Stop and then Start.
+ void Reset();
+
+ // Get/Set the task to be run when this timer expires. NOTE: The caller of
+ // set_task must be careful to ensure that the old task is properly deleted.
+ Task* task() const { return timer_.task(); }
+ void set_task(Task* task) {
+ timer_.set_task(task);
+ owns_task_ = true;
+ }
+
+ // Sets the task, but marks it so it shouldn't be deleted by the SimpleTimer.
+ void set_unowned_task(Task* task) {
+ timer_.set_task(task);
+ owns_task_ = false;
+ }
+
+ protected:
+ SimpleTimer(TimeDelta delay, Task* task, bool repeating);
+
+ private:
+ Timer timer_;
+
+ // Whether we need to clean up the Task* object for this Timer when
+ // we are deallocated. Defaults to true.
+ bool owns_task_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SimpleTimer);
+};
+
+// A simple, one-shot timer. The task is run after the specified delay once
+// the Start method is called. The task is deleted when the timer object is
+// destroyed.
+class OneShotTimer : public SimpleTimer {
+ public:
+ // The task must be set using set_task before calling Start.
+ explicit OneShotTimer(TimeDelta delay)
+ : SimpleTimer(delay, NULL, false) {
+ }
+ // If task is null, then it must be set using set_task before calling Start.
+ OneShotTimer(TimeDelta delay, Task* task)
+ : SimpleTimer(delay, task, false) {
+ }
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(OneShotTimer);
+};
+
+// A simple, repeating timer. The task is run at the specified interval once
+// the Start method is called. The task is deleted when the timer object is
+// destroyed.
+class RepeatingTimer : public SimpleTimer {
+ public:
+ // The task must be set using set_task before calling Start.
+ explicit RepeatingTimer(TimeDelta interval)
+ : SimpleTimer(interval, NULL, true) {
+ }
+ // If task is null, then it must be set using set_task before calling Start.
+ RepeatingTimer(TimeDelta interval, Task* task)
+ : SimpleTimer(interval, task, true) {
+ }
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(RepeatingTimer);
+};
+
+#endif // BASE_TIMER_H_
diff --git a/base/timer_unittest.cc b/base/timer_unittest.cc
new file mode 100644
index 0000000..814836b
--- /dev/null
+++ b/base/timer_unittest.cc
@@ -0,0 +1,332 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/timer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+ class TimerTest : public testing::Test {
+ };
+};
+
+// A base class timer task that sanity-checks timer functionality and counts
+// the number of times it has run. Handles all message loop and memory
+// management issues.
+class TimerTask : public Task {
+ public:
+ // Runs all timers to completion. This returns only after all timers have
+ // finished firing.
+ static void RunTimers();
+
+ // Creates a new timer. If |repeating| is true, the timer will repeat 10
+ // times before terminating.
+ //
+ // All timers are managed on the message loop of the thread that calls this
+ // function the first time.
+ TimerTask(int delay, bool repeating);
+
+ virtual ~TimerTask();
+
+ int iterations() const { return iterations_; }
+ const Timer* timer() const { return timer_; }
+
+ // Resets the timer, if it exists.
+ void Reset();
+
+ // Task
+ virtual void Run();
+
+ protected:
+ // Shuts down the message loop if necessary.
+ static void QuitMessageLoop();
+
+ private:
+ static MessageLoop* message_loop() {
+ static MessageLoop* loop = MessageLoop::current();
+ return loop;
+ }
+
+ static int timer_count_;
+ static bool loop_running_;
+
+ bool timer_running_;
+ int delay_;
+ TimeTicks start_ticks_;
+ int iterations_;
+ Timer* timer_;
+};
+
+// static
+void TimerTask::RunTimers() {
+ if (timer_count_ && !loop_running_) {
+ loop_running_ = true;
+ message_loop()->Run();
+ }
+}
+
+TimerTask::TimerTask(int delay, bool repeating)
+ : timer_running_(false),
+ delay_(delay),
+ start_ticks_(TimeTicks::Now()),
+ iterations_(0),
+ timer_(NULL) {
+ Reset(); // This will just set up the variables to indicate we have a
+ // running timer.
+ timer_ = message_loop()->timer_manager()->StartTimer(delay, this, repeating);
+}
+
+TimerTask::~TimerTask() {
+ if (timer_) {
+ message_loop()->timer_manager()->StopTimer(timer_);
+ delete timer_;
+ }
+ if (timer_running_) {
+ timer_running_ = false;
+ if (--timer_count_ <= 0)
+ QuitMessageLoop();
+ }
+}
+
+void TimerTask::Reset() {
+ if (!timer_running_) {
+ timer_running_ = true;
+ ++timer_count_;
+ }
+ if (timer_) {
+ start_ticks_ = TimeTicks::Now();
+ message_loop()->timer_manager()->ResetTimer(timer_);
+ }
+}
+
+void TimerTask::Run() {
+ ++iterations_;
+
+ // Test that we fired on or after the delay, not before.
+ const TimeTicks ticks = TimeTicks::Now();
+ EXPECT_LE(delay_, (ticks - start_ticks_).InMilliseconds());
+ // Note: Add the delay rather than using the ticks recorded.
+ // Repeating timers have already started ticking before
+ // this callback; we pretend they started *now*, then
+ // it might seem like they fire early, when they do not.
+ start_ticks_ += TimeDelta::FromMilliseconds(delay_);
+
+ // If we're done running, shut down the message loop.
+ if (timer_->repeating() && (iterations_ < 10))
+ return; // Iterate 10 times before terminating.
+ message_loop()->timer_manager()->StopTimer(timer_);
+ timer_running_ = false;
+ if (--timer_count_ <= 0)
+ QuitMessageLoop();
+}
+
+// static
+void TimerTask::QuitMessageLoop() {
+ if (loop_running_) {
+ message_loop()->Quit();
+ loop_running_ = false;
+ }
+}
+
+int TimerTask::timer_count_ = 0;
+bool TimerTask::loop_running_ = false;
+
+// A task that deletes itself when run.
+class DeletingTask : public TimerTask {
+ public:
+ DeletingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
+
+ // Task
+ virtual void Run();
+};
+
+void DeletingTask::Run() {
+ delete this;
+
+ // Can't call TimerTask::Run() here, we've destroyed ourselves.
+}
+
+// A class that resets another TimerTask when run.
+class ResettingTask : public TimerTask {
+ public:
+ ResettingTask(int delay, bool repeating, TimerTask* task)
+ : TimerTask(delay, repeating),
+ task_(task) {
+ }
+
+ virtual void Run();
+
+ private:
+ TimerTask* task_;
+};
+
+void ResettingTask::Run() {
+ task_->Reset();
+
+ TimerTask::Run();
+}
+
+// A class that quits the message loop when run.
+class QuittingTask : public TimerTask {
+ public:
+ QuittingTask(int delay, bool repeating) : TimerTask(delay, repeating) { }
+
+ virtual void Run();
+};
+
+void QuittingTask::Run() {
+ QuitMessageLoop();
+
+ TimerTask::Run();
+}
+
+void RunTimerTest() {
+ // Make sure oneshot timers work correctly.
+ TimerTask task1(100, false);
+ TimerTask::RunTimers();
+ EXPECT_EQ(1, task1.iterations());
+
+ // Make sure repeating timers work correctly.
+ TimerTask task2(10, true);
+ TimerTask task3(100, true);
+ TimerTask::RunTimers();
+ EXPECT_EQ(10, task2.iterations());
+ EXPECT_EQ(10, task3.iterations());
+}
+
+TEST(TimerTest, TimerComparison) {
+ // Make sure TimerComparison sorts correctly.
+ const TimerTask task1(10, false);
+ const Timer* timer1 = task1.timer();
+ const TimerTask task2(200, false);
+ const Timer* timer2 = task2.timer();
+ TimerComparison comparison;
+ EXPECT_FALSE(comparison(timer1, timer2));
+ EXPECT_TRUE(comparison(timer2, timer1));
+}
+
+TEST(TimerTest, TimerCase) {
+ RunTimerTest();
+}
+
+TEST(TimerTest, BrokenTimerCase) {
+ // Simulate faulty early-firing timers. The tasks in RunTimerTest should
+ // nevertheless be invoked after their specified delays, regardless of when
+ // WM_TIMER fires.
+ TimerManager* manager = MessageLoop::current()->timer_manager();
+ manager->set_use_broken_delay(true);
+ RunTimerTest();
+ manager->set_use_broken_delay(false);
+}
+
+TEST(TimerTest, DeleteFromRun) {
+ // Make sure TimerManager correctly handles a Task that deletes itself when
+ // run.
+ DeletingTask* deleting_task1 = new DeletingTask(50, true);
+ TimerTask timer_task(150, false);
+ DeletingTask* deleting_task2 = new DeletingTask(250, true);
+ TimerTask::RunTimers();
+ EXPECT_EQ(1, timer_task.iterations());
+}
+
+TEST(TimerTest, Reset) {
+ // Make sure resetting a timer after it has fired works.
+ TimerTask timer_task1(250, false);
+ TimerTask timer_task2(100, true);
+ ResettingTask resetting_task1(600, false, &timer_task1);
+ TimerTask::RunTimers();
+ EXPECT_EQ(2, timer_task1.iterations());
+ EXPECT_EQ(10, timer_task2.iterations());
+
+ // Make sure resetting a timer before it has fired works. This will reset
+ // two timers, then stop the message loop between when they should have
+ // finally fired.
+ TimerTask timer_task3(100, false);
+ TimerTask timer_task4(600, false);
+ ResettingTask resetting_task3(50, false, &timer_task3);
+ ResettingTask resetting_task4(50, false, &timer_task4);
+ QuittingTask quitting_task(300, false);
+ TimerTask::RunTimers();
+ EXPECT_EQ(1, timer_task3.iterations());
+ EXPECT_EQ(0, timer_task4.iterations());
+}
+
+TEST(TimerTest, FifoOrder) {
+ // Creating timers with the same timeout should
+ // always compare to result in FIFO ordering.
+
+ // Derive from the timer so that we can set it's fire time.
+ // We have to do this, because otherwise, it's possible for
+ // two timers, created back to back, to have different times,
+ // and in that case, we aren't really testing what we want
+ // to test!
+ class MockTimer : public Timer {
+ public:
+ MockTimer(int delay) : Timer(delay, NULL, false) {}
+ void set_fire_time(const Time& t) { fire_time_ = t; }
+ };
+
+ class MockTimerManager : public TimerManager {
+ public:
+ // Pops the most-recent to fire timer and returns its timer id.
+ // Returns -1 if there are no timers in the list.
+ int pop() {
+ int rv = -1;
+ Timer* top = PeekTopTimer();
+ if (top) {
+ rv = top->id();
+ StopTimer(top);
+ delete top;
+ }
+ return rv;
+ }
+ };
+
+ MockTimer t1(0);
+ MockTimer t2(0);
+ t2.set_fire_time(t1.fire_time());
+ TimerComparison comparison;
+ EXPECT_TRUE(comparison(&t2, &t1));
+
+ // Issue a tight loop of timers; most will have the
+ // same timestamp; some will not. Either way, since
+ // all are created with delay(0), the second timer
+ // must always be greater than the first. Then, pop
+ // all the timers and verify that it's a FIFO list.
+ MockTimerManager manager;
+ const int kNumTimers = 1024;
+ for (int i=0; i < kNumTimers; i++)
+ Timer* timer = manager.StartTimer(0, NULL, false);
+
+ int last_id = -1;
+ int new_id = 0;
+ while((new_id = manager.pop()) > 0)
+ EXPECT_GT(new_id, last_id);
+} \ No newline at end of file
diff --git a/base/tracked.cc b/base/tracked.cc
new file mode 100644
index 0000000..59c26f6
--- /dev/null
+++ b/base/tracked.cc
@@ -0,0 +1,113 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/tracked.h"
+
+#include "base/string_util.h"
+#include "base/tracked_objects.h"
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+void Location::Write(bool display_filename, bool display_function_name,
+ std::string* output) const {
+ StringAppendF(output, "%s[%d] ",
+ display_filename ? file_name_ : "line",
+ line_number_);
+
+ if (display_function_name) {
+ WriteFunctionName(output);
+ output->push_back(' ');
+ }
+}
+
+void Location::WriteFunctionName(std::string* output) const {
+ // Translate "<" to "&lt;" for HTML safety.
+ // TODO(jar): Support ASCII or html for logging in ASCII.
+ for (const char *p = function_name_; *p; p++) {
+ switch (*p) {
+ case '<':
+ output->append("&lt;");
+ break;
+
+ case '>':
+ output->append("&gt;");
+ break;
+
+ default:
+ output->push_back(*p);
+ break;
+ }
+ }
+}
+
+//------------------------------------------------------------------------------
+
+#ifndef TRACK_ALL_TASK_OBJECTS
+
+Tracked::Tracked() {}
+Tracked::~Tracked() {}
+void Tracked::SetBirthPlace(const Location& from_here) {}
+bool Tracked::MissingBirthplace() const { return false; }
+
+#else
+
+Tracked::Tracked() : tracked_births_(NULL), tracked_birth_time_(Time::Now()) {
+ if (!ThreadData::IsActive())
+ return;
+ SetBirthPlace(Location("NoFunctionName", "NeedToSetBirthPlace", -1));
+}
+
+Tracked::~Tracked() {
+ if (!ThreadData::IsActive() || !tracked_births_)
+ return;
+ ThreadData::current()->TallyADeath(*tracked_births_,
+ Time::Now() - tracked_birth_time_);
+}
+
+void Tracked::SetBirthPlace(const Location& from_here) {
+ if (!ThreadData::IsActive())
+ return;
+ if (tracked_births_)
+ tracked_births_->ForgetBirth();
+ ThreadData* current_thread_data = ThreadData::current();
+ if (!current_thread_data)
+ return; // Shutdown started, and this thread wasn't registered.
+ tracked_births_ = current_thread_data->FindLifetime(from_here);
+ tracked_births_->RecordBirth();
+}
+
+bool Tracked::MissingBirthplace() const {
+ return -1 == tracked_births_->location().line_number();
+}
+
+#endif // NDEBUG
+
+} // namespace tracked_objects
+
diff --git a/base/tracked.h b/base/tracked.h
new file mode 100644
index 0000000..1b5d22a
--- /dev/null
+++ b/base/tracked.h
@@ -0,0 +1,129 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//------------------------------------------------------------------------------
+// Tracked is the base class for all tracked objects. During construction, it
+// registers the fact that an instance was created, and at destruction time, it
+// records that event. The instance may be tagged with a name, which is refered
+// to as its Location. The Location is a file and line number, most
+// typically indicated where the object was constructed. In some cases, as the
+// object's significance is refined (for example, a Task object is augmented to
+// do additonal things), its Location may be redefined to that later location.
+
+// Tracking includes (for each instance) recording the birth thread, death
+// thread, and duration of life (from construction to destruction). All this
+// data is accumulated and filtered for review at about:objects.
+
+#ifndef BASE_TRACKED_H__
+#define BASE_TRACKED_H__
+
+#include <string>
+
+#include "base/time.h"
+
+#ifndef NDEBUG
+#define TRACK_ALL_TASK_OBJECTS
+#endif
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+// Location provides basic info where of an object was constructed, or was
+// significantly brought to life.
+
+class Location {
+ public:
+ // Constructor should be called with a long-lived char*, such as __FILE__.
+ // It assumes the provided value will persist as a global constant, and it
+ // will not make a copy of it.
+ Location(const char* function_name, const char* file_name, int line_number)
+ : function_name_(function_name),
+ file_name_(file_name),
+ line_number_(line_number) { }
+
+ // Provide a default constructor for easy of debugging.
+ Location()
+ : function_name_("Unknown"),
+ file_name_("Unknown"),
+ line_number_(-1) { }
+
+ // Comparison operator for insertion into a std::map<> hash tables.
+ // All we need is *some* (any) hashing distinction. Strings should already
+ // be unique, so we don't bother with strcmp or such.
+ // Use line number as the primary key (because it is fast, and usually gets us
+ // a difference), and then pointers as secondary keys (just to get some
+ // distinctions).
+ bool operator < (const Location& other) const {
+ if (line_number_ != other.line_number_)
+ return line_number_ < other.line_number_;
+ if (file_name_ != other.file_name_)
+ return file_name_ != other.file_name_;
+ return function_name_ < other.function_name_;
+ }
+
+ const char* function_name() const { return function_name_; }
+ const char* file_name() const { return file_name_; }
+ int line_number() const { return line_number_; }
+
+ void Write(bool display_filename, bool display_function_name,
+ std::string* output) const;
+
+ // Write function_name_ in HTML with '<' and '>' properly encoded.
+ void WriteFunctionName(std::string* output) const;
+
+ private:
+ const char* const function_name_;
+ const char* const file_name_;
+ const int line_number_;
+};
+
+
+//------------------------------------------------------------------------------
+
+
+class Births;
+
+class Tracked {
+ public:
+ Tracked();
+ virtual ~Tracked();
+ void SetBirthPlace(const Location& from_here);
+
+ bool MissingBirthplace() const;
+
+ private:
+ Births* tracked_births_; // At same birthplace, and same thread.
+ const Time tracked_birth_time_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Tracked);
+};
+
+} // namespace tracked_objects
+
+#endif // BASE_TRACKED_H__
diff --git a/base/tracked_objects.cc b/base/tracked_objects.cc
new file mode 100644
index 0000000..61c3639
--- /dev/null
+++ b/base/tracked_objects.cc
@@ -0,0 +1,918 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/tracked_objects.h"
+
+#include "base/string_util.h"
+
+namespace tracked_objects {
+
+// a TLS index to the TrackRegistry for the current thread.
+// static
+TLSSlot ThreadData::tls_index_ = -1;
+
+//------------------------------------------------------------------------------
+// Death data tallies durations when a death takes place.
+
+void DeathData::RecordDeath(const TimeDelta& duration) {
+ ++count_;
+ life_duration_ += duration;
+ int64 milliseconds = duration.InMilliseconds();
+ square_duration_ += milliseconds * milliseconds;
+}
+
+int DeathData::AverageMsDuration() const {
+ return static_cast<int>(life_duration_.InMilliseconds() / count_);
+}
+
+double DeathData::StandardDeviation() const {
+ double average = AverageMsDuration();
+ double variance = static_cast<float>(square_duration_)/count_
+ - average * average;
+ return sqrt(variance);
+}
+
+
+void DeathData::AddDeathData(const DeathData& other) {
+ count_ += other.count_;
+ life_duration_ += other.life_duration_;
+ square_duration_ += other.square_duration_;
+}
+
+void DeathData::Write(std::string* output) const {
+ if (!count_)
+ return;
+ if (1 == count_)
+ StringAppendF(output, "(1)Life in %dms ", count_, AverageMsDuration());
+ else
+ StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration());
+}
+
+void DeathData::Clear() {
+ count_ = 0;
+ life_duration_ = TimeDelta();
+ square_duration_ = 0;
+}
+
+//------------------------------------------------------------------------------
+
+BirthOnThread::BirthOnThread(const Location& location)
+ : location_(location),
+ birth_thread_(ThreadData::current()) { }
+
+//------------------------------------------------------------------------------
+Births::Births(const Location& location)
+ : BirthOnThread(location),
+ birth_count_(0) { }
+
+//------------------------------------------------------------------------------
+// ThreadData maintains the central data for all births and death.
+
+// static
+ThreadData* ThreadData::first_ = NULL;
+// static
+Lock ThreadData::list_lock_;
+
+// static
+ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED;
+
+ThreadData::ThreadData() : message_loop_(MessageLoop::current()) {}
+
+// static
+ThreadData* ThreadData::current() {
+ if (-1 == tls_index_)
+ return NULL; // not yet initialized.
+
+ ThreadData* registry =
+ static_cast<ThreadData*>(ThreadLocalStorage::Get(tls_index_));
+ if (!registry) {
+ // We have to create a new registry for ThreadData.
+ bool too_late_to_create = false;
+ {
+ registry = new ThreadData;
+ AutoLock lock(list_lock_);
+ // Use lock to insure we have most recent status.
+ if (!IsActive()) {
+ too_late_to_create = true;
+ } else {
+ // Use lock to insert into list.
+ registry->next_ = first_;
+ first_ = registry;
+ }
+ } // Release lock.
+ if (too_late_to_create) {
+ delete registry;
+ registry = NULL;
+ } else {
+ ThreadLocalStorage::Set(tls_index_, registry);
+ }
+ }
+ return registry;
+}
+
+// Do mininimal fixups for searching function names.
+static std::string UnescapeQuery(const std::string& query) {
+ std::string result;
+ for (size_t i = 0; i < query.size(); i++) {
+ char next = query[i];
+ if ('%' == next && i + 2 < query.size()) {
+ std::string hex = query.substr(i + 1, 2);
+ char replacement = '\0';
+ // Only bother with "<", ">", and " ".
+ if (LowerCaseEqualsASCII(hex, "3c"))
+ replacement ='<';
+ else if (LowerCaseEqualsASCII(hex, "3e"))
+ replacement = '>';
+ else if (hex == "20")
+ replacement = ' ';
+ if (replacement) {
+ next = replacement;
+ i += 2;
+ }
+ }
+ result.push_back(next);
+ }
+ return result;
+}
+
+// static
+void ThreadData::WriteHTML(const std::string& query, std::string* output) {
+ if (!ThreadData::IsActive())
+ return; // Not yet initialized.
+
+ DCHECK(ThreadData::current());
+
+ output->append("<html><head><title>About Objects");
+ std::string escaped_query = UnescapeQuery(query);
+ if (!escaped_query.empty())
+ output->append(" - " + escaped_query);
+ output->append("</title></head><body><pre>");
+
+ DataCollector collected_data; // Gather data.
+ collected_data.AddListOfLivingObjects(); // Add births that are still alive.
+
+ // Data Gathering is complete. Now to sort/process/render.
+ DataCollector::Collection* collection = collected_data.collection();
+
+ // Create filtering and sort comparison object.
+ Comparator comparator;
+ bool display_details = comparator.ParseQuery(escaped_query);
+
+ // Filter out acceptable (matching) instances.
+ DataCollector::Collection match_array;
+ for (DataCollector::Collection::iterator it = collection->begin();
+ it != collection->end(); ++it) {
+ if (comparator.Acceptable(*it))
+ match_array.push_back(*it);
+ }
+
+ comparator.Sort(&match_array);
+
+ WriteHTMLTotalAndSubtotals(match_array, comparator, output);
+
+ comparator.Clear(); // Delete tiebreaker_ instances.
+
+ output->append("</pre></body></html>");
+}
+
+// static
+void ThreadData::WriteHTMLTotalAndSubtotals(
+ const DataCollector::Collection& match_array,
+ const Comparator& comparator,
+ std::string* output) {
+ if (!match_array.size()) {
+ output->append("There were no tracked matches.");
+ } else {
+ // Aggregate during printing
+ Aggregation totals;
+ for (size_t i = 0; i < match_array.size(); ++i) {
+ totals.AddDeathSnapshot(match_array[i]);
+ }
+ output->append("Aggregate Stats: ");
+ totals.Write(output);
+ output->append("<hr><hr>");
+
+ Aggregation subtotals;
+ for (size_t i = 0; i < match_array.size(); ++i) {
+ if (0 == i || !comparator.Equivalent(match_array[i - 1],
+ match_array[i])) {
+ // Print group's defining characteristics.
+ comparator.WriteSortGrouping(match_array[i], output);
+ output->append("<br><br>");
+ }
+ comparator.WriteSnapshot(match_array[i], output);
+ output->append("<br>");
+ subtotals.AddDeathSnapshot(match_array[i]);
+ if (i + 1 >= match_array.size() ||
+ !comparator.Equivalent(match_array[i],
+ match_array[i + 1])) {
+ // Print aggregate stats for the group.
+ output->append("<br>");
+ subtotals.Write(output);
+ output->append("<br><hr><br>");
+ subtotals.Clear();
+ }
+ }
+ }
+}
+
+Births* ThreadData::FindLifetime(const Location& location) {
+ if (!message_loop_) // In case message loop wasn't yet around...
+ message_loop_ = MessageLoop::current(); // Find it now.
+
+ BirthMap::iterator it = birth_map_.find(location);
+ if (it != birth_map_.end())
+ return it->second;
+ Births* tracker = new Births(location);
+
+ // Lock since the map may get relocated now, and other threads sometimes
+ // snapshot it (but they lock before copying it).
+ AutoLock lock(lock_);
+ birth_map_[location] = tracker;
+ return tracker;
+}
+
+void ThreadData::TallyADeath(const Births& lifetimes,
+ const TimeDelta& duration) {
+ if (!message_loop_) // In case message loop wasn't yet around...
+ message_loop_ = MessageLoop::current(); // Find it now.
+
+ DeathMap::iterator it = death_map_.find(&lifetimes);
+ if (it != death_map_.end()) {
+ it->second.RecordDeath(duration);
+ return;
+ }
+
+ AutoLock lock(lock_); // Lock since the map may get relocated now.
+ death_map_[&lifetimes].RecordDeath(duration);
+}
+
+// static
+ThreadData* ThreadData::first() {
+ AutoLock lock(list_lock_);
+ return first_;
+}
+
+const std::string ThreadData::ThreadName() const {
+ if (message_loop_)
+ return message_loop_->thread_name();
+ return "ThreadWithoutMessageLoop";
+}
+
+// This may be called from another thread.
+void ThreadData::SnapshotBirthMap(BirthMap *output) const {
+ AutoLock lock(*const_cast<Lock*>(&lock_));
+ for (BirthMap::const_iterator it = birth_map_.begin();
+ it != birth_map_.end(); ++it)
+ (*output)[it->first] = it->second;
+}
+
+// This may be called from another thread.
+void ThreadData::SnapshotDeathMap(DeathMap *output) const {
+ AutoLock lock(*const_cast<Lock*>(&lock_));
+ for (DeathMap::const_iterator it = death_map_.begin();
+ it != death_map_.end(); ++it)
+ (*output)[it->first] = it->second;
+}
+
+void ThreadData::RunOnAllThreads(void (*function)()) {
+ ThreadData* list = first(); // Get existing list.
+
+ std::vector<MessageLoop*> message_loops;
+ for (ThreadData* it = list; it; it = it->next()) {
+ if (current() != it && it->message_loop())
+ message_loops.push_back(it->message_loop());
+ }
+
+ ThreadSafeDownCounter* counter =
+ new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us!
+
+ HANDLE completion_handle = CreateEvent(NULL, false, false, NULL);
+ // Tell all other threads to run.
+ for (size_t i = 0; i < message_loops.size(); ++i)
+ message_loops[i]->PostTask(FROM_HERE,
+ new RunTheStatic(function, completion_handle, counter));
+
+ // Also run Task on our thread.
+ RunTheStatic local_task(function, completion_handle, counter);
+ local_task.Run();
+
+ WaitForSingleObject(completion_handle, INFINITE);
+ int ret_val = CloseHandle(completion_handle);
+ DCHECK(ret_val);
+}
+
+// static
+bool ThreadData::StartTracking(bool status) {
+#ifndef TRACK_ALL_TASK_OBJECTS
+ return false; // Not compiled in.
+#endif
+
+ if (!status) {
+ AutoLock lock(list_lock_);
+ DCHECK(status_ == ACTIVE || status_ == SHUTDOWN);
+ status_ = SHUTDOWN;
+ return true;
+ }
+ TLSSlot tls_index = ThreadLocalStorage::Alloc();
+ AutoLock lock(list_lock_);
+ DCHECK(status_ == UNINITIALIZED);
+ tls_index_ = tls_index;
+ CHECK(-1 != tls_index_);
+ status_ = ACTIVE;
+ return true;
+}
+
+// static
+bool ThreadData::IsActive() {
+ return status_ == ACTIVE;
+}
+
+// static
+void ThreadData::ShutdownMultiThreadTracking() {
+ // Using lock, guarantee that no new ThreadData instances will be created.
+ if (!StartTracking(false))
+ return;
+
+ RunOnAllThreads(ShutdownDisablingFurtherTracking);
+
+ // Now the *only* threads that might change the database are the threads with
+ // no messages loops. They might still be adding data to their birth records,
+ // but since no objects are deleted on those threads, there will be no further
+ // access to to cross-thread data.
+ // We could do a cleanup on all threads except for the ones without
+ // MessageLoops, but we won't bother doing cleanup (destruction of data) yet.
+ return;
+}
+
+// static
+void ThreadData::ShutdownSingleThreadedCleanup() {
+ // We must be single threaded... but be careful anyway.
+ if (!StartTracking(false))
+ return;
+ ThreadData* thread_data_list;
+ {
+ AutoLock lock(list_lock_);
+ thread_data_list = first_;
+ first_ = NULL;
+ }
+
+ while (thread_data_list) {
+ ThreadData* next_thread_data = thread_data_list;
+ thread_data_list = thread_data_list->next();
+
+ for (BirthMap::iterator it = next_thread_data->birth_map_.begin();
+ next_thread_data->birth_map_.end() != it; ++it)
+ delete it->second; // Delete the Birth Records.
+ next_thread_data->birth_map_.clear();
+ next_thread_data->death_map_.clear();
+ delete next_thread_data; // Includes all Death Records.
+ }
+
+ CHECK(-1 != tls_index_);
+ ThreadLocalStorage::Free(tls_index_);
+ tls_index_ = -1;
+ status_ = UNINITIALIZED;
+}
+
+// static
+void ThreadData::ShutdownDisablingFurtherTracking() {
+ // Redundantly set status SHUTDOWN on this thread.
+ if (!StartTracking(false))
+ return;
+}
+
+
+//------------------------------------------------------------------------------
+
+ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count)
+ : remaining_count_(count) {
+ DCHECK(remaining_count_ > 0);
+}
+
+bool ThreadData::ThreadSafeDownCounter::LastCaller() {
+ {
+ AutoLock lock(lock_);
+ if (--remaining_count_)
+ return false;
+ } // Release lock, so we can delete everything in this instance.
+ delete this;
+ return true;
+}
+
+//------------------------------------------------------------------------------
+
+ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function,
+ HANDLE completion_handle,
+ ThreadSafeDownCounter* counter)
+ : function_(function),
+ completion_handle_(completion_handle),
+ counter_(counter) {
+}
+
+void ThreadData::RunTheStatic::Run() {
+ function_();
+ if (counter_->LastCaller())
+ SetEvent(completion_handle_);
+ }
+
+
+//------------------------------------------------------------------------------
+// Individual 3-tuple of birth (place and thread) along with death thread, and
+// the accumulated stats for instances (DeathData).
+
+Snapshot::Snapshot(const BirthOnThread& birth_on_thread,
+ const ThreadData& death_thread,
+ const DeathData& death_data)
+ : birth_(&birth_on_thread),
+ death_thread_(&death_thread),
+ death_data_(death_data) {
+}
+
+Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count)
+ : birth_(&birth_on_thread),
+ death_thread_(NULL),
+ death_data_(DeathData(count)) {
+}
+
+const std::string Snapshot::DeathThreadName() const {
+ if (death_thread_)
+ return death_thread_->ThreadName();
+ return "Still_Alive";
+}
+
+void Snapshot::Write(std::string* output) const {
+ death_data_.Write(output);
+ StringAppendF(output, "%s->%s ",
+ birth_->birth_thread()->ThreadName().c_str(),
+ death_thread_->ThreadName().c_str());
+ birth_->location().Write(true, true, output);
+}
+
+void Snapshot::Add(const Snapshot& other) {
+ death_data_.AddDeathData(other.death_data_);
+}
+
+//------------------------------------------------------------------------------
+// DataCollector
+
+DataCollector::DataCollector() {
+ DCHECK(ThreadData::IsActive());
+
+ ThreadData* my_list = ThreadData::current()->first();
+
+ count_of_contributing_threads_ = 0;
+ for (ThreadData* thread_data = my_list;
+ thread_data;
+ thread_data = thread_data->next()) {
+ ++count_of_contributing_threads_;
+ }
+
+ // Gather data serially. A different constructor could be used to do in
+ // parallel, and then invoke an OnCompletion task.
+ for (ThreadData* thread_data = my_list;
+ thread_data;
+ thread_data = thread_data->next()) {
+ Append(*thread_data);
+ }
+}
+
+void DataCollector::Append(const ThreadData& thread_data) {
+ // Get copy of data (which is done under ThreadData's lock).
+ ThreadData::BirthMap birth_map;
+ thread_data.SnapshotBirthMap(&birth_map);
+ ThreadData::DeathMap death_map;
+ thread_data.SnapshotDeathMap(&death_map);
+
+ // Use our lock to protect our accumulation activity.
+ AutoLock lock(accumulation_lock_);
+
+ DCHECK(count_of_contributing_threads_);
+
+ for (ThreadData::DeathMap::const_iterator it = death_map.begin();
+ it != death_map.end(); ++it) {
+ collection_.push_back(Snapshot(*it->first, thread_data, it->second));
+ global_birth_count_[it->first] -= it->first->birth_count();
+ }
+
+ for (ThreadData::BirthMap::const_iterator it = birth_map.begin();
+ it != birth_map.end(); ++it) {
+ global_birth_count_[it->second] += it->second->birth_count();
+ }
+
+ --count_of_contributing_threads_;
+}
+
+DataCollector::Collection* DataCollector::collection() {
+ DCHECK(!count_of_contributing_threads_);
+ return &collection_;
+}
+
+void DataCollector::AddListOfLivingObjects() {
+ DCHECK(!count_of_contributing_threads_);
+ for (BirthCount::iterator it = global_birth_count_.begin();
+ it != global_birth_count_.end(); ++it) {
+ if (it->second > 0)
+ collection_.push_back(Snapshot(*it->first, it->second));
+ }
+}
+
+//------------------------------------------------------------------------------
+// Aggregation
+
+void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) {
+ AddBirth(snapshot.birth());
+ death_threads_[snapshot.death_thread()]++;
+ AddDeathData(snapshot.death_data());
+}
+
+void Aggregation::AddBirths(const Births& births) {
+ AddBirth(births);
+ birth_count_ += births.birth_count();
+}
+void Aggregation::AddBirth(const BirthOnThread& birth) {
+ AddBirthPlace(birth.location());
+ birth_threads_[birth.birth_thread()]++;
+}
+
+void Aggregation::AddBirthPlace(const Location& location) {
+ locations_[location]++;
+ birth_files_[location.file_name()]++;
+}
+
+void Aggregation::Write(std::string* output) const {
+ if (locations_.size() == 1) {
+ locations_.begin()->first.Write(true, true, output);
+ } else {
+ StringAppendF(output, "%d Locations. ", locations_.size());
+ if (birth_files_.size() > 1)
+ StringAppendF(output, "%d Files. ", birth_files_.size());
+ else
+ StringAppendF(output, "All born in %s. ",
+ birth_files_.begin()->first.c_str());
+ }
+
+ if (birth_threads_.size() > 1)
+ StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size());
+ else
+ StringAppendF(output, "All born on %s. ",
+ birth_threads_.begin()->first->ThreadName().c_str());
+
+ if (death_threads_.size() > 1) {
+ StringAppendF(output, "%d DeathThreads. ", death_threads_.size());
+ } else {
+ if (death_threads_.begin()->first)
+ StringAppendF(output, "All deleted on %s. ",
+ death_threads_.begin()->first->ThreadName().c_str());
+ else
+ output->append("All these objects are still alive.");
+ }
+
+ if (birth_count_ > 1)
+ StringAppendF(output, "Births=%d ", birth_count_);
+
+ DeathData::Write(output);
+}
+
+void Aggregation::Clear() {
+ birth_count_ = 0;
+ birth_files_.clear();
+ locations_.clear();
+ birth_threads_.clear();
+ DeathData::Clear();
+ death_threads_.clear();
+}
+
+//------------------------------------------------------------------------------
+// Comparison object for sorting.
+
+Comparator::Comparator()
+ : selector_(NIL),
+ tiebreaker_(NULL),
+ combined_selectors_(0),
+ use_tiebreaker_for_sort_only_(false) {}
+
+void Comparator::Clear() {
+ if (tiebreaker_) {
+ tiebreaker_->Clear();
+ delete tiebreaker_;
+ tiebreaker_ = NULL;
+ }
+ use_tiebreaker_for_sort_only_ = false;
+ selector_ = NIL;
+}
+
+void Comparator::Sort(DataCollector::Collection* collection) const {
+ std::sort(collection->begin(), collection->end(), *this);
+}
+
+
+bool Comparator::operator()(const Snapshot& left,
+ const Snapshot& right) const {
+ switch (selector_) {
+ case BIRTH_THREAD:
+ if (left.birth_thread() != right.birth_thread() &&
+ left.birth_thread()->ThreadName() !=
+ right.birth_thread()->ThreadName())
+ return left.birth_thread()->ThreadName() <
+ right.birth_thread()->ThreadName();
+ break;
+
+ case DEATH_THREAD:
+ if (left.death_thread() != right.death_thread() &&
+ left.DeathThreadName() !=
+ right.DeathThreadName()) {
+ if (!left.death_thread())
+ return true;
+ if (!right.death_thread())
+ return false;
+ return left.DeathThreadName() <
+ right.DeathThreadName();
+ }
+ break;
+
+ case BIRTH_FILE:
+ if (left.location().file_name() != right.location().file_name()) {
+ int comp = strcmp(left.location().file_name(),
+ right.location().file_name());
+ if (comp)
+ return 0 > comp;
+ }
+ break;
+
+ case BIRTH_FUNCTION:
+ if (left.location().function_name() != right.location().function_name()) {
+ int comp = strcmp(left.location().function_name(),
+ right.location().function_name());
+ if (comp)
+ return 0 > comp;
+ }
+ break;
+
+ case BIRTH_LINE:
+ if (left.location().line_number() != right.location().line_number())
+ return left.location().line_number() <
+ right.location().line_number();
+ break;
+
+ case COUNT:
+ if (left.count() != right.count())
+ return left.count() > right.count(); // Sort large at front of vector.
+ break;
+
+ case AVERAGE_DURATION:
+ if (left.AverageMsDuration() != right.AverageMsDuration())
+ return left.AverageMsDuration() > right.AverageMsDuration();
+ break;
+ }
+ if (tiebreaker_)
+ return tiebreaker_->operator()(left, right);
+ return false;
+}
+
+bool Comparator::Equivalent(const Snapshot& left,
+ const Snapshot& right) const {
+ switch (selector_) {
+ case BIRTH_THREAD:
+ if (left.birth_thread() != right.birth_thread() &&
+ left.birth_thread()->ThreadName() !=
+ right.birth_thread()->ThreadName())
+ return false;
+ break;
+
+ case DEATH_THREAD:
+ if (left.death_thread() != right.death_thread() &&
+ left.DeathThreadName() !=
+ right.DeathThreadName())
+ return false;
+ break;
+
+ case BIRTH_FILE:
+ if (left.location().file_name() != right.location().file_name()) {
+ int comp = strcmp(left.location().file_name(),
+ right.location().file_name());
+ if (comp)
+ return false;
+ }
+ break;
+
+ case BIRTH_FUNCTION:
+ if (left.location().function_name() != right.location().function_name()) {
+ int comp = strcmp(left.location().function_name(),
+ right.location().function_name());
+ if (comp)
+ return false;
+ }
+ break;
+
+ case COUNT:
+ if (left.count() != right.count())
+ return false;
+ break;
+
+ case AVERAGE_DURATION:
+ if (left.life_duration() != right.life_duration())
+ return false;
+ break;
+ }
+ if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
+ return tiebreaker_->Equivalent(left, right);
+ return true;
+}
+
+bool Comparator::Acceptable(const Snapshot& sample) const {
+ if (required_.size()) {
+ switch (selector_) {
+ case BIRTH_THREAD:
+ if (sample.birth_thread()->ThreadName().find(required_)
+ == std::string.npos)
+ return false;
+ break;
+
+ case DEATH_THREAD:
+ if (sample.DeathThreadName().find(required_) == std::string.npos)
+ return false;
+ break;
+
+ case BIRTH_FILE:
+ if (!strstr(sample.location().file_name(), required_.c_str()))
+ return false;
+ break;
+
+ case BIRTH_FUNCTION:
+ if (!strstr(sample.location().function_name(), required_.c_str()))
+ return false;
+ break;
+ }
+ }
+ if (tiebreaker_ && !use_tiebreaker_for_sort_only_)
+ return tiebreaker_->Acceptable(sample);
+ return true;
+}
+
+void Comparator::SetTiebreaker(Selector selector, const std::string required) {
+ if (selector == selector_ || NIL == selector)
+ return;
+ combined_selectors_ |= selector;
+ if (NIL == selector_) {
+ selector_ = selector;
+ if (required.size())
+ required_ = required;
+ return;
+ }
+ if (tiebreaker_) {
+ if (use_tiebreaker_for_sort_only_) {
+ Comparator* temp = new Comparator;
+ temp->tiebreaker_ = tiebreaker_;
+ tiebreaker_ = temp;
+ }
+ } else {
+ tiebreaker_ = new Comparator;
+ DCHECK(!use_tiebreaker_for_sort_only_);
+ }
+ tiebreaker_->SetTiebreaker(selector, required);
+}
+
+bool Comparator::IsGroupedBy(Selector selector) const {
+ return 0 != (selector & combined_selectors_);
+}
+
+void Comparator::SetSubgroupTiebreaker(Selector selector) {
+ if (selector == selector_ || NIL == selector)
+ return;
+ if (!tiebreaker_) {
+ use_tiebreaker_for_sort_only_ = true;
+ tiebreaker_ = new Comparator;
+ tiebreaker_->SetTiebreaker(selector, "");
+ } else {
+ tiebreaker_->SetSubgroupTiebreaker(selector);
+ }
+}
+
+void Comparator::ParseKeyphrase(const std::string key_phrase) {
+ static std::map<const std::string, Selector> key_map;
+ static bool initialized = false;
+ if (!initialized) {
+ initialized = true;
+ key_map["count"] = COUNT;
+ key_map["duration"] = AVERAGE_DURATION;
+ key_map["birth"] = BIRTH_THREAD;
+ key_map["death"] = DEATH_THREAD;
+ key_map["file"] = BIRTH_FILE;
+ key_map["function"] = BIRTH_FUNCTION;
+ key_map["line"] = BIRTH_LINE;
+ }
+
+ std::string required;
+ size_t equal_offset = key_phrase.find('=', 0);
+ if (key_phrase.npos != equal_offset)
+ required = key_phrase.substr(equal_offset + 1, key_phrase.npos);
+ std::string keyword(key_phrase.substr(0, equal_offset));
+ keyword = StringToLowerASCII(keyword);
+ if (key_map.end() == key_map.find(keyword))
+ return;
+ SetTiebreaker(key_map[keyword], required);
+}
+
+bool Comparator::ParseQuery(const std::string query) {
+ for (size_t i = 0; i < query.size();) {
+ size_t slash_offset = query.find('/', i);
+ ParseKeyphrase(query.substr(i, slash_offset - i));
+ if (query.npos == slash_offset)
+ break;
+ i = slash_offset + 1;
+ }
+
+ // Select subgroup ordering (if we want to display the subgroup)
+ SetSubgroupTiebreaker(COUNT);
+ SetSubgroupTiebreaker(AVERAGE_DURATION);
+ SetSubgroupTiebreaker(BIRTH_THREAD);
+ SetSubgroupTiebreaker(DEATH_THREAD);
+ SetSubgroupTiebreaker(BIRTH_FUNCTION);
+ SetSubgroupTiebreaker(BIRTH_FILE);
+ SetSubgroupTiebreaker(BIRTH_LINE);
+
+ return true;
+}
+
+bool Comparator::WriteSortGrouping(const Snapshot& sample,
+ std::string* output) const {
+ bool wrote_data = false;
+ switch (selector_) {
+ case NIL:
+ break;
+
+ case BIRTH_THREAD:
+ StringAppendF(output, "All new on %s ",
+ sample.birth_thread()->ThreadName().c_str());
+ wrote_data = true;
+ break;
+
+ case DEATH_THREAD:
+ if (sample.death_thread())
+ StringAppendF(output, "All deleted on %s ",
+ sample.DeathThreadName().c_str());
+ else
+ output->append("All still alive ");
+ wrote_data = true;
+ break;
+
+ case BIRTH_FILE:
+ StringAppendF(output, "All born in %s ",
+ sample.location().file_name());
+ break;
+
+ case BIRTH_FUNCTION:
+ output->append("All born in ");
+ sample.location().WriteFunctionName(output);
+ output->push_back(' ');
+ break;
+ }
+ if (tiebreaker_ && !use_tiebreaker_for_sort_only_) {
+ wrote_data |= tiebreaker_->WriteSortGrouping(sample, output);
+ }
+ return wrote_data;
+}
+
+void Comparator::WriteSnapshot(const Snapshot& sample,
+ std::string* output) const {
+ sample.death_data().Write(output);
+ if (!(combined_selectors_ & BIRTH_THREAD) ||
+ !(combined_selectors_ & DEATH_THREAD))
+ StringAppendF(output, "%s->%s ",
+ (combined_selectors_ & BIRTH_THREAD) ? "*" :
+ sample.birth().birth_thread()->ThreadName().c_str(),
+ (combined_selectors_ & DEATH_THREAD) ? "*" :
+ sample.DeathThreadName().c_str());
+ sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE),
+ !(combined_selectors_ & BIRTH_FUNCTION),
+ output);
+}
+
+} // namespace tracked_objects
diff --git a/base/tracked_objects.h b/base/tracked_objects.h
new file mode 100644
index 0000000..23638e1
--- /dev/null
+++ b/base/tracked_objects.h
@@ -0,0 +1,509 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TRACKED_OBJECTS_H_
+#define BASE_TRACKED_OBJECTS_H_
+
+//------------------------------------------------------------------------------
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/thread_local_storage.h"
+#include "base/tracked.h"
+
+
+namespace tracked_objects {
+
+//------------------------------------------------------------------------------
+// For a specific thread, and a specific birth place, the collection of all
+// death info (with tallies for each death thread, to prevent access conflicts).
+class ThreadData;
+class BirthOnThread {
+ public:
+ explicit BirthOnThread(const Location& location);
+
+ const Location location() const { return location_; }
+ const ThreadData* birth_thread() const { return birth_thread_; }
+
+ private:
+ // File/lineno of birth. This defines the essence of the type, as the context
+ // of the birth (construction) often tell what the item is for. This field
+ // is const, and hence safe to access from any thread.
+ const Location location_;
+
+ // The thread that records births into this object. Only this thread is
+ // allowed to access birth_count_ (which changes over time).
+ const ThreadData* birth_thread_; // The thread this birth took place on.
+
+ DISALLOW_EVIL_CONSTRUCTORS(BirthOnThread);
+};
+
+//------------------------------------------------------------------------------
+// A class for accumulating counts of births (without bothering with a map<>).
+
+class Births: public BirthOnThread {
+ public:
+ explicit Births(const Location& location);
+
+ int birth_count() const { return birth_count_; }
+
+ // When we have a birth we update the count for this BirhPLace.
+ void RecordBirth() { ++birth_count_; }
+
+ // When a birthplace is changed (updated), we need to decrement the counter
+ // for the old instance.
+ void ForgetBirth() { --birth_count_; } // We corrected a birth place.
+
+ private:
+ // The number of births on this thread for our location_.
+ int birth_count_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Births);
+};
+
+//------------------------------------------------------------------------------
+// Basic info summarizing multiple destructions of an object with a single
+// birthplace (fixed Location). Used both on specific threads, and also used
+// in snapshots when integrating assembled data.
+
+class DeathData {
+ public:
+ // Default initializer.
+ DeathData() : count_(0), square_duration_(0) {}
+
+ // When deaths have not yet taken place, and we gather data from all the
+ // threads, we create DeathData stats that tally the number of births without
+ // a corrosponding death.
+ explicit DeathData(int count) : count_(count), square_duration_(0) {}
+
+ void RecordDeath(const TimeDelta& duration);
+
+ // Metrics accessors.
+ int count() const { return count_; }
+ TimeDelta life_duration() const { return life_duration_; }
+ int64 square_duration() const { return square_duration_; }
+ int AverageMsDuration() const;
+ double StandardDeviation() const;
+
+ // Accumulate metrics from other into this.
+ void AddDeathData(const DeathData& other);
+
+ // Simple print of internal state.
+ void Write(std::string* output) const;
+
+ void Clear();
+
+ private:
+ int count_; // Number of destructions.
+ TimeDelta life_duration_; // Sum of all lifetime durations.
+ int64 square_duration_; // Sum of squares in milliseconds.
+};
+
+//------------------------------------------------------------------------------
+// A temporary collection of data that can be sorted and summarized. It is
+// gathered (carefully) from many threads. Instances are held in arrays and
+// processed, filtered, and rendered.
+// The source of this data was collected on many threads, and is asynchronously
+// changing. The data in this instance is not asynchronously changing.
+
+class Snapshot {
+ public:
+ // When snapshotting a full life cycle set (birth-to-death), use this:
+ Snapshot(const BirthOnThread& birth_on_thread, const ThreadData& death_thread,
+ const DeathData& death_data);
+
+ // When snapshotting a birth, with no death yet, use this:
+ Snapshot(const BirthOnThread& birth_on_thread, int count);
+
+
+ const ThreadData* birth_thread() const { return birth_->birth_thread(); }
+ const Location location() const { return birth_->location(); }
+ const BirthOnThread& birth() const { return *birth_; }
+ const ThreadData* death_thread() const {return death_thread_; }
+ const DeathData& death_data() const { return death_data_; }
+ const std::string DeathThreadName() const;
+
+ int count() const { return death_data_.count(); }
+ TimeDelta life_duration() const { return death_data_.life_duration(); }
+ int64 square_duration() const { return death_data_.square_duration(); }
+ int AverageMsDuration() const { return death_data_.AverageMsDuration(); }
+
+ void Snapshot::Write(std::string* output) const;
+
+ void Add(const Snapshot& other);
+
+ private:
+ const BirthOnThread* birth_; // Includes Location and birth_thread.
+ const ThreadData* death_thread_;
+ DeathData death_data_;
+};
+//------------------------------------------------------------------------------
+// DataCollector is a container class for Snapshot and BirthOnThread count
+// items. It protects the gathering under locks, so that it could be called via
+// Posttask on any threads, such as all the target threads in parallel.
+
+class DataCollector {
+ public:
+ typedef std::vector<const Snapshot> Collection;
+
+ // Construct with a list of how many threads should contribute. This helps us
+ // determine (in the async case) when we are done with all contributions.
+ DataCollector();
+
+ // Add all stats from the indicated thread into our arrays. This function is
+ // mutex protected, and *could* be called from any threads (although current
+ // implementation serialized calls to Append).
+ void Append(const ThreadData& thread_data);
+
+ // After the accumulation phase, the following access is to process data.
+ Collection* collection();
+
+ // After collection of death data is complete, we can add entries for all the
+ // remaining living objects.
+ void AddListOfLivingObjects();
+
+ private:
+ // This instance may be provided to several threads to contribute data. The
+ // following counter tracks how many more threads will contribute. When it is
+ // zero, then all asynchronous contributions are complete, and locked access
+ // is no longer needed.
+ int count_of_contributing_threads_;
+
+ // The array that we collect data into.
+ Collection collection_;
+
+ // The total number of births recorded at each location for which we have not
+ // seen a death count.
+ typedef std::map<const BirthOnThread*, int> BirthCount;
+ BirthCount global_birth_count_;
+
+ Lock accumulation_lock_; // Protects access during accumulation phase.
+
+ DISALLOW_EVIL_CONSTRUCTORS(DataCollector);
+};
+
+//------------------------------------------------------------------------------
+// Aggregation contains summaries (totals and subtotals) of groups of Snapshot
+// instances to provide printing of these collections on a single line.
+
+class Aggregation: public DeathData {
+ public:
+ Aggregation() : birth_count_(0) {}
+
+ void AddDeathSnapshot(const Snapshot& snapshot);
+ void AddBirths(const Births& births);
+ void AddBirth(const BirthOnThread& birth);
+ void AddBirthPlace(const Location& location);
+ void Write(std::string* output) const;
+ void Clear();
+
+ private:
+ int birth_count_;
+ std::map<std::string, int> birth_files_;
+ std::map<Location, int> locations_;
+ std::map<const ThreadData*, int> birth_threads_;
+ DeathData death_data_;
+ std::map<const ThreadData*, int> death_threads_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Aggregation);
+};
+
+//------------------------------------------------------------------------------
+// Comparator does the comparison of Snapshot instances. It is
+// used to order the instances in a vector. It orders them into groups (for
+// aggregation), and can also order instances within the groups (for detailed
+// rendering of the instances).
+
+class Comparator {
+ public:
+ enum Selector {
+ NIL = 0,
+ BIRTH_THREAD = 1,
+ DEATH_THREAD = 2,
+ BIRTH_FILE = 4,
+ BIRTH_FUNCTION = 8,
+ BIRTH_LINE = 16,
+ COUNT = 32,
+ AVERAGE_DURATION = 64,
+ TOTAL_DURATION = 128,
+ };
+
+ explicit Comparator();
+
+ // Reset the comparator to a NIL selector. Reset() and recursively delete any
+ // tiebreaker_ entries. NOTE: We can't use a standard destructor, because
+ // the sort algorithm makes copies of this object, and then deletes them,
+ // which would cause problems (either we'd make expensive deep copies, or we'd
+ // do more thna one delete on a tiebreaker_.
+ void Clear();
+
+ // The less() operator for sorting the array via std::sort().
+ bool operator()(const Snapshot& left, const Snapshot& right) const;
+
+ void Sort(DataCollector::Collection* collection) const;
+
+ // Check to see if the items are sort equivalents (should be aggregated).
+ bool Equivalent(const Snapshot& left, const Snapshot& right) const;
+
+ // Check to see if all required fields are present in the given sample.
+ bool Acceptable(const Snapshot& sample) const;
+
+ // A comparator can be refined by specifying what to do if the selected basis
+ // for comparison is insufficient to establish an ordering. This call adds
+ // the indicated attribute as the new "least significant" basis of comparison.
+ void SetTiebreaker(Selector selector, const std::string required);
+
+ // Indicate if this instance is set up to sort by the given Selector, thereby
+ // putting that information in the SortGrouping, so it is not needed in each
+ // printed line.
+ bool IsGroupedBy(Selector selector) const;
+
+ // Using the tiebreakers as set above, we mostly get an ordering, which
+ // equivalent groups. If those groups are displayed (rather than just being
+ // aggregated, then the following is used to order them (within the group).
+ void SetSubgroupTiebreaker(Selector selector);
+
+ // Translate a keyword and restriction in URL path to a selector for sorting.
+ void ParseKeyphrase(const std::string key_phrase);
+
+ // Parse a query in an about:objects URL to decide on sort ordering.
+ bool ParseQuery(const std::string query);
+
+ // Output a header line that can be used to indicated what items will be
+ // collected in the group. It lists all (potentially) tested attributes and
+ // their values (in the sample item).
+ bool WriteSortGrouping(const Snapshot& sample, std::string* output) const;
+
+ // Output a sample, with SortGroup details not displayed.
+ void WriteSnapshot(const Snapshot& sample, std::string* output) const;
+
+ private:
+ // The selector directs this instance to compare based on the specified
+ // members of the tested elements.
+ enum Selector selector_;
+
+ // For filtering into acceptable and unacceptable snapshot instance, the
+ // following is required to be a substring of the selector_ field.
+ std::string required_;
+
+ // If this instance can't decide on an ordering, we can consult a tie-breaker
+ // which may have a different basis of comparison.
+ Comparator* tiebreaker_;
+
+ // We or together all the selectors we sort on (not counting sub-group
+ // selectors), so that we can tell if we've decided to group on any given
+ // criteria.
+ int combined_selectors_;
+
+ // Some tiebreakrs are for subgroup ordering, and not for basic ordering (in
+ // preparation for aggregation). The subgroup tiebreakers are not consulted
+ // when deciding if two items are in equivalent groups. This flag tells us
+ // to ignore the tiebreaker when doing Equivalent() testing.
+ bool use_tiebreaker_for_sort_only_;
+};
+
+
+//------------------------------------------------------------------------------
+// For each thread, we have a ThreadData that stores all tracking info generated
+// on this thread. This prevents the need for locking as data accumulates.
+
+class ThreadData {
+ public:
+ typedef std::map<Location, Births*> BirthMap;
+ typedef std::map<const Births*, DeathData> DeathMap;
+
+ ThreadData();
+
+ // Using Thread Local Store, find the current instance for collecting data.
+ // If an instance does not exist, construct one (and remember it for use on
+ // this thread.
+ // If shutdown has already started, and we don't yet have an instance, then
+ // return null.
+ static ThreadData* current();
+
+ // For a given about:objects URL, develop resulting HTML, and append to
+ // output.
+ static void WriteHTML(const std::string& query, std::string* output);
+
+ // For a given accumulated array of results, use the comparator to sort and
+ // subtotal, writing the results to the output.
+ static void WriteHTMLTotalAndSubtotals(
+ const DataCollector::Collection& match_array,
+ const Comparator& comparator, std::string* output);
+
+ // In this thread's data, find a place to record a new birth.
+ Births* FindLifetime(const Location& location);
+
+ // Find a place to record a death on this thread.
+ void TallyADeath(const Births& lifetimes, const TimeDelta& duration);
+
+ // (Thread safe) Get start of list of instances.
+ static ThreadData* first();
+ // Iterate through the null terminated list of instances.
+ ThreadData* next() const { return next_; }
+
+ MessageLoop* message_loop() const { return message_loop_; }
+ const std::string ThreadName() const;
+
+ // Using our lock, make a copy of the specified maps. These calls may arrive
+ // from non-local threads.
+ void SnapshotBirthMap(BirthMap *output) const;
+ void SnapshotDeathMap(DeathMap *output) const;
+
+ static void RunOnAllThreads(void (*Func)());
+
+ // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN,
+ // based on argument being true or false respectively.
+ // IF tracking is not compiled in, this function will return false.
+ static bool StartTracking(bool status);
+ static bool IsActive();
+
+ // WARNING: ONLY call this function when all MessageLoops are still intact for
+ // all registered threads. IF you call it later, you will crash.
+ // Note: You don't need to call it at all, and you can wait till you are
+ // single threaded (again) to do the cleanup via
+ // ShutdownSingleThreadedCleanup().
+ // Start the teardown (shutdown) process in a multi-thread mode by disabling
+ // further additions to thread database on all threads. First it makes a
+ // local (locked) change to prevent any more threads from registering. Then
+ // it Posts a Task to all registered threads to be sure they are aware that no
+ // more accumulation can take place.
+ static void ShutdownMultiThreadTracking();
+
+ // WARNING: ONLY call this function when you are running single threaded
+ // (again) and all message loops and threads have terminated. Until that
+ // point some threads may still attempt to write into our data structures.
+ // Delete recursively all data structures, starting with the list of
+ // ThreadData instances.
+ static void ShutdownSingleThreadedCleanup();
+
+ private:
+ // Current allowable states of the tracking system. The states always
+ // proceed towards SHUTDOWN, and never go backwards.
+ enum Status {
+ UNINITIALIZED,
+ ACTIVE,
+ SHUTDOWN,
+ };
+
+ // A class used to count down which is accessed by several threads. This is
+ // used to make sure RunOnAllThreads() actually runs a task on the expected
+ // count of threads.
+ class ThreadSafeDownCounter {
+ public:
+ // Constructor sets the count, once and for all.
+ explicit ThreadSafeDownCounter(size_t count);
+
+ // Decrement the count, and return true if we hit zero. Also delete this
+ // instance automatically when we hit zero.
+ bool LastCaller();
+
+ private:
+ size_t remaining_count_;
+ Lock lock_; // protect access to remaining_count_.
+ };
+
+ // A Task class that runs a static method supplied, and checks to see if this
+ // is the last tasks instance (on last thread) that will run the method.
+ // IF this is the last run, then the supplied event is signalled.
+ class RunTheStatic : public Task {
+ public:
+ typedef void (*FunctionPointer)();
+ RunTheStatic(FunctionPointer function,
+ HANDLE completion_handle,
+ ThreadSafeDownCounter* counter);
+ // Run the supplied static method, and optionally set the event.
+ void Run();
+
+ private:
+ FunctionPointer function_;
+ HANDLE completion_handle_;
+ // Make sure enough tasks are called before completion is signaled.
+ ThreadSafeDownCounter* counter_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RunTheStatic);
+ };
+
+ // Each registered thread is called to set status_ to SHUTDOWN.
+ // This is done redundantly on every registered thread because it is not
+ // protected by a mutex. Running on all threads guarantees we get the
+ // notification into the memory cache of all possible threads.
+ static void ShutdownDisablingFurtherTracking();
+
+ // We use thread local store to identify which ThreadData to interact with.
+ static TLSSlot tls_index_ ;
+
+ // Link to the most recently created instance (starts a null terminated list).
+ static ThreadData* first_;
+ // Protection for access to first_.
+ static Lock list_lock_;
+
+
+ // We set status_ to SHUTDOWN when we shut down the tracking service. This
+ // setting is redundantly established by all participating
+ // threads so that we are *guaranteed* (without locking) that all threads
+ // can "see" the status and avoid additional calls into the service.
+ static Status status_;
+
+ // Link to next instance (null terminated list). Used to globally track all
+ // registered instances (corresponds to all registered threads where we keep
+ // data).
+ ThreadData* next_;
+
+ // The message loop where tasks needing to access this instance's private data
+ // should be directed. Since some threads have no message loop, some
+ // instances have data that can't be (safely) modified externally.
+ MessageLoop* message_loop_;
+
+ // A map used on each thread to keep track of Births on this thread.
+ // This map should only be accessed on the thread it was constructed on.
+ // When a snapshot is needed, this structure can be locked in place for the
+ // duration of the snapshotting activity.
+ BirthMap birth_map_;
+
+ // Similar to birth_map_, this records informations about death of tracked
+ // instances (i.e., when a tracked instance was destroyed on this thread).
+ DeathMap death_map_;
+
+ // Lock to protect *some* access to BirthMap and DeathMap. We only use
+ // locking protection when we are growing the maps, or using an iterator. We
+ // only do writes to members from this thread, so the updates of values are
+ // atomic. Folks can read from other threads, and get (via races) new or old
+ // data, but that is considered acceptable errors (mis-information).
+ Lock lock_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadData);
+};
+
+} // namespace tracked_objects
+
+#endif // BASE_TRACKED_OBJECTS_H_
diff --git a/base/tracked_objects_test.cc b/base/tracked_objects_test.cc
new file mode 100644
index 0000000..be30745
--- /dev/null
+++ b/base/tracked_objects_test.cc
@@ -0,0 +1,122 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Test of classes in the tracked_objects.h classes.
+
+#include "base/tracked_objects.h"
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace tracked_objects {
+
+class TrackedObjectsTest : public testing::Test {
+};
+
+TEST(TrackedObjectsTest, MinimalStartupShutdown) {
+ // Minimal test doesn't even create any tasks.
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
+ ThreadData* data = ThreadData::current();
+ EXPECT_TRUE(ThreadData::first()); // Now class was constructed.
+ EXPECT_TRUE(data);
+ EXPECT_TRUE(!data->next());
+ EXPECT_EQ(data, ThreadData::current());
+ ThreadData::BirthMap birth_map;
+ data->SnapshotBirthMap(&birth_map);
+ EXPECT_EQ(0u, birth_map.size());
+ ThreadData::DeathMap death_map;
+ data->SnapshotDeathMap(&death_map);
+ EXPECT_EQ(0u, death_map.size());
+ ThreadData::ShutdownSingleThreadedCleanup();
+
+ // Do it again, just to be sure we reset state completely.
+ ThreadData::StartTracking(true);
+ EXPECT_FALSE(ThreadData::first()); // No activity even on this thread.
+ data = ThreadData::current();
+ EXPECT_TRUE(ThreadData::first()); // Now class was constructed.
+ EXPECT_TRUE(data);
+ EXPECT_TRUE(!data->next());
+ EXPECT_EQ(data, ThreadData::current());
+ birth_map.clear();
+ data->SnapshotBirthMap(&birth_map);
+ EXPECT_EQ(0u, birth_map.size());
+ death_map.clear();
+ data->SnapshotDeathMap(&death_map);
+ EXPECT_EQ(0u, death_map.size());
+ ThreadData::ShutdownSingleThreadedCleanup();
+}
+
+class NoopTask : public Task {
+ public:
+ void Run() {}
+};
+
+TEST(TrackedObjectsTest, TinyStartupShutdown) {
+ if (!ThreadData::StartTracking(true))
+ return;
+
+ // Instigate tracking on a single task, or our thread.
+ NoopTask task;
+
+ const ThreadData* data = ThreadData::first();
+ EXPECT_TRUE(data);
+ EXPECT_TRUE(!data->next());
+ EXPECT_EQ(data, ThreadData::current());
+ ThreadData::BirthMap birth_map;
+ data->SnapshotBirthMap(&birth_map);
+ EXPECT_EQ(1u, birth_map.size()); // 1 birth location.
+ EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth.
+ ThreadData::DeathMap death_map;
+ data->SnapshotDeathMap(&death_map);
+ EXPECT_EQ(0u, death_map.size()); // No deaths.
+
+
+ // Now instigate a birth, and a death.
+ delete new NoopTask;
+
+ birth_map.clear();
+ data->SnapshotBirthMap(&birth_map);
+ EXPECT_EQ(1u, birth_map.size()); // 1 birth location.
+ EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births.
+ death_map.clear();
+ data->SnapshotDeathMap(&death_map);
+ EXPECT_EQ(1u, death_map.size()); // 1 location.
+ EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death.
+
+ // The births were at the same location as the one known death.
+ EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first);
+
+ ThreadData::ShutdownSingleThreadedCleanup();
+}
+
+
+} // namespace tracked_objects
+
diff --git a/base/tuple.h b/base/tuple.h
new file mode 100644
index 0000000..6222a41
--- /dev/null
+++ b/base/tuple.h
@@ -0,0 +1,687 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_TUPLE_H__
+#define BASE_TUPLE_H__
+
+// Traits ----------------------------------------------------------------------
+//
+// A simple traits class for tuple arguments.
+//
+// ValueType: the bare, nonref version of a type (same as the type for nonrefs).
+// RefType: the ref version of a type (same as the type for refs).
+// ParamType: what type to pass to functions (refs should not be constified).
+
+template <class P>
+struct TupleTraits {
+ typedef P ValueType;
+ typedef P& RefType;
+ typedef const P& ParamType;
+};
+
+template <class P>
+struct TupleTraits<P&> {
+ typedef P ValueType;
+ typedef P& RefType;
+ typedef P& ParamType;
+};
+
+// Tuple -----------------------------------------------------------------------
+//
+// This set of classes is useful for bundling 0 or more heterogeneous data types
+// into a single variable. The advantage of this is that it greatly simplifies
+// function objects that need to take an arbitrary number of parameters; see
+// RunnableMethod and IPC::MessageWithTuple.
+//
+// Tuple0 is supplied to act as a 'void' type. It can be used, for example, when
+// dispatching to a function that accepts no arguments (see the Dispatchers
+// below).
+// Tuple1<A> is rarely useful. One such use is when A is non-const ref that you
+// want filled by the dispatchee, and the tuple is merely a container for that
+// output (a "tier"). See MakeRefTuple and its usages.
+
+struct Tuple0 {
+ typedef Tuple0 ValueTuple;
+ typedef Tuple0 RefTuple;
+};
+
+template <class A>
+struct Tuple1 {
+ public:
+ typedef A TypeA;
+ typedef Tuple1<typename TupleTraits<A>::ValueType> ValueTuple;
+ typedef Tuple1<typename TupleTraits<A>::RefType> RefTuple;
+
+ Tuple1() {}
+ explicit Tuple1(typename TupleTraits<A>::ParamType a) : a(a) {}
+
+ A a;
+};
+
+template <class A, class B>
+struct Tuple2 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef Tuple2<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType> ValueTuple;
+ typedef Tuple2<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType> RefTuple;
+
+ Tuple2() {}
+ Tuple2(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b)
+ : a(a), b(b) {
+ }
+
+ A a;
+ B b;
+};
+
+template <class A, class B, class C>
+struct Tuple3 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef Tuple3<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType> ValueTuple;
+ typedef Tuple3<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType> RefTuple;
+
+ Tuple3() {}
+ Tuple3(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c)
+ : a(a), b(b), c(c){
+ }
+
+ A a;
+ B b;
+ C c;
+};
+
+template <class A, class B, class C, class D>
+struct Tuple4 {
+ public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef Tuple4<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType> ValueTuple;
+ typedef Tuple4<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType> RefTuple;
+
+ Tuple4() {}
+ Tuple4(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d)
+ : a(a), b(b), c(c), d(d) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+};
+
+template <class A, class B, class C, class D, class E>
+struct Tuple5 {
+public:
+ typedef A TypeA;
+ typedef B TypeB;
+ typedef C TypeC;
+ typedef D TypeD;
+ typedef E TypeE;
+ typedef Tuple5<typename TupleTraits<A>::ValueType,
+ typename TupleTraits<B>::ValueType,
+ typename TupleTraits<C>::ValueType,
+ typename TupleTraits<D>::ValueType,
+ typename TupleTraits<E>::ValueType> ValueTuple;
+ typedef Tuple5<typename TupleTraits<A>::RefType,
+ typename TupleTraits<B>::RefType,
+ typename TupleTraits<C>::RefType,
+ typename TupleTraits<D>::RefType,
+ typename TupleTraits<E>::RefType> RefTuple;
+
+ Tuple5() {}
+ Tuple5(typename TupleTraits<A>::ParamType a,
+ typename TupleTraits<B>::ParamType b,
+ typename TupleTraits<C>::ParamType c,
+ typename TupleTraits<D>::ParamType d,
+ typename TupleTraits<E>::ParamType e)
+ : a(a), b(b), c(c), d(d), e(e) {
+ }
+
+ A a;
+ B b;
+ C c;
+ D d;
+ E e;
+};
+
+// Tuple creators -------------------------------------------------------------
+//
+// Helper functions for constructing tuples while inferring the template
+// argument types.
+
+inline Tuple0 MakeTuple() {
+ return Tuple0();
+}
+
+template <class A>
+inline Tuple1<A> MakeTuple(const A& a) {
+ return Tuple1<A>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A, B> MakeTuple(const A& a, const B& b) {
+ return Tuple2<A, B>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A, B, C> MakeTuple(const A& a, const B& b, const C& c) {
+ return Tuple3<A, B, C>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A, B, C, D> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d) {
+ return Tuple4<A, B, C, D>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A, B, C, D, E> MakeTuple(const A& a, const B& b, const C& c,
+ const D& d, const E& e) {
+ return Tuple5<A, B, C, D, E>(a, b, c, d, e);
+}
+
+// The following set of helpers make what Boost refers to as "Tiers" - a tuple
+// of references.
+
+template <class A>
+inline Tuple1<A&> MakeRefTuple(A& a) {
+ return Tuple1<A&>(a);
+}
+
+template <class A, class B>
+inline Tuple2<A&, B&> MakeRefTuple(A& a, B& b) {
+ return Tuple2<A&, B&>(a, b);
+}
+
+template <class A, class B, class C>
+inline Tuple3<A&, B&, C&> MakeRefTuple(A& a, B& b, C& c) {
+ return Tuple3<A&, B&, C&>(a, b, c);
+}
+
+template <class A, class B, class C, class D>
+inline Tuple4<A&, B&, C&, D&> MakeRefTuple(A& a, B& b, C& c, D& d) {
+ return Tuple4<A&, B&, C&, D&>(a, b, c, d);
+}
+
+template <class A, class B, class C, class D, class E>
+inline Tuple5<A&, B&, C&, D&, E&> MakeRefTuple(A& a, B& b, C& c, D& d, E& e) {
+ return Tuple5<A&, B&, C&, D&, E&>(a, b, c, d, e);
+}
+
+// Dispatchers ----------------------------------------------------------------
+//
+// Helper functions that call the given method on an object, with the unpacked
+// tuple arguments. Notice that they all have the same number of arguments,
+// so you need only write:
+// DispatchToMethod(object, &Object::method, args);
+// This is very useful for templated dispatchers, since they don't need to know
+// what type |args| is.
+
+// Non-Static Dispatchers with no out params.
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) {
+ (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) {
+ (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg) {
+ (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple2<A, B>& arg) {
+ (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<A, B, C>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<A, B, C, D>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<A, B, C, D, E>& arg) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Static Dispatchers with no out params.
+
+template <class Function>
+inline void DispatchToFunction(Function function, const Tuple0& arg) {
+ (*function)();
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const A& arg) {
+ (*function)(arg);
+}
+
+template <class Function, class A>
+inline void DispatchToFunction(Function function, const Tuple1<A>& arg) {
+ (*function)(arg.a);
+}
+
+template<class Function, class A, class B>
+inline void DispatchToFunction(Function function, const Tuple2<A, B>& arg) {
+ (*function)(arg.a, arg.b);
+}
+
+template<class Function, class A, class B, class C>
+inline void DispatchToFunction(Function function, const Tuple3<A, B, C>& arg) {
+ (*function)(arg.a, arg.b, arg.c);
+}
+
+template<class Function, class A, class B, class C, class D>
+inline void DispatchToFunction(Function function,
+ const Tuple4<A, B, C, D>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class Function, class A, class B, class C, class D, class E>
+inline void DispatchToFunction(Function function,
+ const Tuple5<A, B, C, D, E>& arg) {
+ (*function)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Dispatchers with 0 out param (as a Tuple0).
+
+template <class ObjT, class Method>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg, Tuple0*) {
+ (obj->*method)();
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) {
+ (obj->*method)(arg);
+}
+
+template <class ObjT, class Method, class A>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1<A>& arg, Tuple0*) {
+ (obj->*method)(arg.a);
+}
+
+template<class ObjT, class Method, class A, class B>
+inline void DispatchToMethod(ObjT* obj, Method method, const Tuple2<A, B>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b);
+}
+
+template<class ObjT, class Method, class A, class B, class C>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<A, B, C>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<A, B, C, D>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d);
+}
+
+template<class ObjT, class Method, class A, class B, class C, class D, class E>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<A, B, C, D, E>& arg, Tuple0*) {
+ (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e);
+}
+
+// Dispatchers with 1 out param.
+
+template<class ObjT, class Method,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple0& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(&out->a);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in, &out->a);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple1<OutA>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a);
+}
+
+// Dispatchers with 2 out params.
+
+template<class ObjT, class Method,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple0& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(&out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple2<OutA, OutB>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b);
+}
+
+// Dispatchers with 3 out params.
+
+template<class ObjT, class Method,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple0& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple3<OutA, OutB, OutC>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c);
+}
+
+// Dispatchers with 4 out params.
+
+template<class ObjT, class Method,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple0& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC, class OutD>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple4<OutA, OutB, OutC, OutD>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+ &out->a, &out->b, &out->c, &out->d);
+}
+
+// Dispatchers with 5 out params.
+
+template<class ObjT, class Method,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple0& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const InA& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<InA>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<InA, InB>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<InA, InB, InC>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+template<class ObjT, class Method, class InA, class InB, class InC, class InD,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<InA, InB, InC, InD>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d,
+ &out->e);
+}
+
+template<class ObjT, class Method,
+ class InA, class InB, class InC, class InD, class InE,
+ class OutA, class OutB, class OutC, class OutD, class OutE>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple5<InA, InB, InC, InD, InE>& in,
+ Tuple5<OutA, OutB, OutC, OutD, OutE>* out) {
+ (obj->*method)(in.a, in.b, in.c, in.d, in.e,
+ &out->a, &out->b, &out->c, &out->d, &out->e);
+}
+
+#endif // BASE_TUPLE_H__
diff --git a/base/values.cc b/base/values.cc
new file mode 100644
index 0000000..1cad628
--- /dev/null
+++ b/base/values.cc
@@ -0,0 +1,583 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/values.h"
+
+///////////////////// Value ////////////////////
+
+Value::~Value() {
+}
+
+// static
+Value* Value::CreateNullValue() {
+ return new Value(TYPE_NULL);
+}
+
+// static
+Value* Value::CreateBooleanValue(bool in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateIntegerValue(int in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateRealValue(double in_value) {
+ return new FundamentalValue(in_value);
+}
+
+// static
+Value* Value::CreateStringValue(const std::wstring& in_value) {
+ return new StringValue(in_value);
+}
+
+// static
+BinaryValue* Value::CreateBinaryValue(char* buffer, size_t size) {
+ return BinaryValue::Create(buffer, size);
+}
+
+bool Value::GetAsBoolean(bool* in_value) const {
+ return false;
+}
+
+bool Value::GetAsInteger(int* in_value) const {
+ return false;
+}
+
+bool Value::GetAsReal(double* in_value) const {
+ return false;
+}
+
+bool Value::GetAsString(std::wstring* in_value) const {
+ return false;
+}
+
+Value* Value::DeepCopy() const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return CreateNullValue();
+}
+
+bool Value::Equals(const Value* other) const {
+ // This method should only be getting called for null Values--all subclasses
+ // need to provide their own implementation;.
+ DCHECK(IsType(TYPE_NULL));
+ return other->IsType(TYPE_NULL);
+}
+
+///////////////////// FundamentalValue ////////////////////
+
+FundamentalValue::~FundamentalValue() {
+}
+
+bool FundamentalValue::GetAsBoolean(bool* out_value) const {
+ if (out_value && IsType(TYPE_BOOLEAN))
+ *out_value = boolean_value_;
+ return (IsType(TYPE_BOOLEAN));
+}
+
+bool FundamentalValue::GetAsInteger(int* out_value) const {
+ if (out_value && IsType(TYPE_INTEGER))
+ *out_value = integer_value_;
+ return (IsType(TYPE_INTEGER));
+}
+
+bool FundamentalValue::GetAsReal(double* out_value) const {
+ if (out_value && IsType(TYPE_REAL))
+ *out_value = real_value_;
+ return (IsType(TYPE_REAL));
+}
+
+Value* FundamentalValue::DeepCopy() const {
+ switch (GetType()) {
+ case TYPE_BOOLEAN:
+ return CreateBooleanValue(boolean_value_);
+
+ case TYPE_INTEGER:
+ return CreateIntegerValue(integer_value_);
+
+ case TYPE_REAL:
+ return CreateRealValue(real_value_);
+
+ default:
+ NOTREACHED();
+ return NULL;
+ }
+}
+
+bool FundamentalValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ switch (GetType()) {
+ case TYPE_BOOLEAN: {
+ bool lhs, rhs;
+ return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs;
+ }
+ case TYPE_INTEGER: {
+ int lhs, rhs;
+ return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs;
+ }
+ case TYPE_REAL: {
+ double lhs, rhs;
+ return GetAsReal(&lhs) && other->GetAsReal(&rhs) && lhs == rhs;
+ }
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+///////////////////// StringValue ////////////////////
+
+StringValue::~StringValue() {
+}
+
+bool StringValue::GetAsString(std::wstring* out_value) const {
+ if (out_value)
+ *out_value = value_;
+ return true;
+}
+
+Value* StringValue::DeepCopy() const {
+ return CreateStringValue(value_);
+}
+
+bool StringValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ std::wstring lhs, rhs;
+ return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs;
+}
+
+///////////////////// BinaryValue ////////////////////
+
+//static
+BinaryValue* BinaryValue::Create(char* buffer, size_t size) {
+ if (!buffer)
+ return NULL;
+
+ return new BinaryValue(buffer, size);
+}
+
+//static
+BinaryValue* BinaryValue::CreateWithCopiedBuffer(char* buffer, size_t size) {
+ if (!buffer)
+ return NULL;
+
+ char* buffer_copy = new char[size];
+ memcpy_s(buffer_copy, size, buffer, size);
+ return new BinaryValue(buffer_copy, size);
+}
+
+
+BinaryValue::BinaryValue(char* buffer, size_t size)
+ : Value(TYPE_BINARY),
+ buffer_(buffer),
+ size_(size) {
+ DCHECK(buffer_);
+}
+
+BinaryValue::~BinaryValue() {
+ DCHECK(buffer_);
+ if (buffer_)
+ delete[] buffer_;
+}
+
+Value* BinaryValue::DeepCopy() const {
+ return CreateWithCopiedBuffer(buffer_, size_);
+}
+
+bool BinaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+ const BinaryValue* other_binary = static_cast<const BinaryValue*>(other);
+ if (other_binary->size_ != size_)
+ return false;
+ return !memcmp(buffer_, other_binary->buffer_, size_);
+}
+
+///////////////////// DictionaryValue ////////////////////
+
+DictionaryValue::~DictionaryValue() {
+ Clear();
+}
+
+void DictionaryValue::Clear() {
+ ValueMap::iterator dict_iterator = dictionary_.begin();
+ while (dict_iterator != dictionary_.end()) {
+ delete dict_iterator->second;
+ ++dict_iterator;
+ }
+
+ dictionary_.clear();
+}
+
+bool DictionaryValue::HasKey(const std::wstring& key) {
+ ValueMap::const_iterator current_entry = dictionary_.find(key);
+ DCHECK((current_entry == dictionary_.end()) || current_entry->second);
+ return current_entry != dictionary_.end();
+}
+
+void DictionaryValue::SetInCurrentNode(const std::wstring& key,
+ Value* in_value) {
+ // If there's an existing value here, we need to delete it, because
+ // we own all our children.
+ if (HasKey(key)) {
+ DCHECK(dictionary_[key] != in_value); // This would be bogus
+ delete dictionary_[key];
+ }
+
+ dictionary_[key] = in_value;
+}
+
+bool DictionaryValue::Set(const std::wstring& path, Value* in_value) {
+ DCHECK(in_value);
+
+ std::wstring key = path;
+
+ size_t delimiter_position = path.find_first_of(L".", 0);
+ // If there isn't a dictionary delimiter in the path, we're done.
+ if (delimiter_position == std::wstring::npos) {
+ SetInCurrentNode(key, in_value);
+ return true;
+ } else {
+ key = path.substr(0, delimiter_position);
+ }
+
+ // Assume that we're indexing into a dictionary.
+ DictionaryValue* entry = NULL;
+ ValueType current_entry_type = TYPE_NULL;
+ if (!HasKey(key) || (dictionary_[key]->GetType() != TYPE_DICTIONARY)) {
+ entry = new DictionaryValue;
+ SetInCurrentNode(key, entry);
+ } else {
+ entry = static_cast<DictionaryValue*>(dictionary_[key]);
+ }
+
+ std::wstring remaining_path = path.substr(delimiter_position + 1);
+ return entry->Set(remaining_path, in_value);
+}
+
+bool DictionaryValue::SetBoolean(const std::wstring& path, bool in_value) {
+ return Set(path, CreateBooleanValue(in_value));
+}
+
+bool DictionaryValue::SetInteger(const std::wstring& path, int in_value) {
+ return Set(path, CreateIntegerValue(in_value));
+}
+
+bool DictionaryValue::SetReal(const std::wstring& path, double in_value) {
+ return Set(path, CreateRealValue(in_value));
+}
+
+bool DictionaryValue::SetString(const std::wstring& path,
+ const std::wstring& in_value) {
+ return Set(path, CreateStringValue(in_value));
+}
+
+bool DictionaryValue::Get(const std::wstring& path, Value** out_value) const {
+ std::wstring key = path;
+
+ size_t delimiter_position = path.find_first_of(L".", 0);
+ if (delimiter_position != std::wstring::npos) {
+ key = path.substr(0, delimiter_position);
+ }
+
+ ValueMap::const_iterator entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
+ return false;
+ Value* entry = entry_iterator->second;
+
+ if (delimiter_position == std::wstring::npos) {
+ if (out_value)
+ *out_value = entry;
+ return true;
+ }
+
+ if (entry->IsType(TYPE_DICTIONARY)) {
+ DictionaryValue* dictionary = static_cast<DictionaryValue*>(entry);
+ return dictionary->Get(path.substr(delimiter_position + 1), out_value);
+ }
+
+ return false;
+}
+
+bool DictionaryValue::GetBoolean(const std::wstring& path,
+ bool* bool_value) const {
+ Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsBoolean(bool_value);
+}
+
+bool DictionaryValue::GetInteger(const std::wstring& path,
+ int* out_value) const {
+ Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsInteger(out_value);
+}
+
+bool DictionaryValue::GetReal(const std::wstring& path,
+ double* out_value) const {
+ Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsReal(out_value);
+}
+
+bool DictionaryValue::GetString(const std::wstring& path,
+ std::wstring* out_value) const {
+ Value* value;
+ if (!Get(path, &value))
+ return false;
+
+ return value->GetAsString(out_value);
+}
+
+bool DictionaryValue::GetBinary(const std::wstring& path,
+ BinaryValue** out_value) const {
+ Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_BINARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<BinaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetDictionary(const std::wstring& path,
+ DictionaryValue** out_value) const {
+ Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_DICTIONARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<DictionaryValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::GetList(const std::wstring& path,
+ ListValue** out_value) const {
+ Value* value;
+ bool result = Get(path, &value);
+ if (!result || !value->IsType(TYPE_LIST))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<ListValue*>(value);
+
+ return true;
+}
+
+bool DictionaryValue::Remove(const std::wstring& path, Value** out_value) {
+ std::wstring key = path;
+
+ size_t delimiter_position = path.find_first_of(L".", 0);
+ if (delimiter_position != std::wstring::npos) {
+ key = path.substr(0, delimiter_position);
+ }
+
+ ValueMap::iterator entry_iterator = dictionary_.find(key);
+ if (entry_iterator == dictionary_.end())
+ return false;
+ Value* entry = entry_iterator->second;
+
+ if (delimiter_position == std::wstring::npos) {
+ if (out_value)
+ *out_value = entry;
+ else
+ delete entry;
+
+ dictionary_.erase(entry_iterator);
+ return true;
+ }
+
+ if (entry->IsType(TYPE_DICTIONARY)) {
+ DictionaryValue* dictionary = static_cast<DictionaryValue*>(entry);
+ return dictionary->Remove(path.substr(delimiter_position + 1), out_value);
+ }
+
+ return false;
+}
+
+Value* DictionaryValue::DeepCopy() const {
+ DictionaryValue* result = new DictionaryValue;
+
+ ValueMap::const_iterator current_entry = dictionary_.begin();
+ while (current_entry != dictionary_.end()) {
+ result->Set(current_entry->first, current_entry->second->DeepCopy());
+ ++current_entry;
+ }
+
+ return result;
+}
+
+bool DictionaryValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const DictionaryValue* other_dict =
+ static_cast<const DictionaryValue*>(other);
+ key_iterator lhs_it(begin_keys());
+ key_iterator rhs_it(other_dict->begin_keys());
+ while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) {
+ Value* lhs;
+ Value* rhs;
+ if (!Get(*lhs_it, &lhs) || !other_dict->Get(*rhs_it, &rhs) ||
+ !lhs->Equals(rhs)) {
+ return false;
+ }
+ ++lhs_it;
+ ++rhs_it;
+ }
+ if (lhs_it != end_keys() || rhs_it != other_dict->end_keys())
+ return false;
+
+ return true;
+}
+
+///////////////////// ListValue ////////////////////
+
+ListValue::~ListValue() {
+ Clear();
+}
+
+void ListValue::Clear() {
+ ValueVector::iterator list_iterator = list_.begin();
+ while (list_iterator != list_.end()) {
+ delete *list_iterator;
+ ++list_iterator;
+ }
+ list_.clear();
+}
+
+bool ListValue::Set(size_t index, Value* in_value) {
+ if (!in_value)
+ return false;
+
+ if (index >= list_.size()) {
+ // Pad out any intermediate indexes with null settings
+ while (index > list_.size())
+ Append(CreateNullValue());
+ Append(in_value);
+ } else {
+ DCHECK(list_[index] != in_value);
+ delete list_[index];
+ list_[index] = in_value;
+ }
+ return true;
+}
+
+bool ListValue::Get(size_t index, Value** out_value) const {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = list_[index];
+
+ return true;
+}
+
+bool ListValue::GetDictionary(size_t index,
+ DictionaryValue** out_value) const {
+ Value* value;
+ bool result = Get(index, &value);
+ if (!result || !value->IsType(TYPE_DICTIONARY))
+ return false;
+
+ if (out_value)
+ *out_value = static_cast<DictionaryValue*>(value);
+
+ return true;
+}
+
+bool ListValue::Remove(size_t index, Value** out_value) {
+ if (index >= list_.size())
+ return false;
+
+ if (out_value)
+ *out_value = list_[index];
+ else
+ delete list_[index];
+
+ ValueVector::iterator entry = list_.begin();
+ entry += index;
+
+ list_.erase(entry);
+ return true;
+}
+
+void ListValue::Append(Value* in_value) {
+ DCHECK(in_value);
+ list_.push_back(in_value);
+}
+
+Value* ListValue::DeepCopy() const {
+ ListValue* result = new ListValue;
+
+ ValueVector::const_iterator current_entry = list_.begin();
+ while (current_entry != list_.end()) {
+ result->Append((*current_entry)->DeepCopy());
+ ++current_entry;
+ }
+
+ return result;
+}
+
+bool ListValue::Equals(const Value* other) const {
+ if (other->GetType() != GetType())
+ return false;
+
+ const ListValue* other_list =
+ static_cast<const ListValue*>(other);
+ const_iterator lhs_it, rhs_it;
+ for (lhs_it = begin(), rhs_it = other_list->begin();
+ lhs_it != end() && rhs_it != other_list->end();
+ ++lhs_it, ++rhs_it) {
+ if (!(*lhs_it)->Equals(*rhs_it))
+ return false;
+ }
+ if (lhs_it != end() || rhs_it != other_list->end())
+ return false;
+
+ return true;
+}
diff --git a/base/values.h b/base/values.h
new file mode 100644
index 0000000..4f135d0
--- /dev/null
+++ b/base/values.h
@@ -0,0 +1,382 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This file specifies a recursive data storage class called Value
+// intended for storing setting and other persistable data.
+// It includes the ability to specify (recursive) lists and dictionaries, so
+// it's fairly expressive. However, the API is optimized for the common case,
+// namely storing a hierarchical tree of simple values. Given a
+// DictionaryValue root, you can easily do things like:
+//
+// root->SetString(L"global.pages.homepage", L"http://goateleporter.com");
+// std::wstring homepage = L"http://google.com"; // default/fallback value
+// root->GetString(L"global.pages.homepage", &homepage);
+//
+// where "global" and "pages" are also DictionaryValues, and "homepage"
+// is a string setting. If some elements of the path didn't exist yet,
+// the SetString() method would create the missing elements and attach them
+// to root before attaching the homepage value.
+
+#ifndef CHROME_COMMON_VALUES_H__
+#define CHROME_COMMON_VALUES_H__
+
+#include <iterator>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+
+class Value;
+class FundamentalValue;
+class StringValue;
+class BinaryValue;
+class DictionaryValue;
+class ListValue;
+
+typedef std::vector<Value*> ValueVector;
+typedef std::map<std::wstring, Value*> ValueMap;
+
+// The Value class is the base class for Values. A Value can be
+// instantiated via the Create*Value() factory methods, or by directly
+// creating instances of the subclasses.
+class Value {
+ public:
+ virtual ~Value();
+
+ // Convenience methods for creating Value objects for various
+ // kinds of values without thinking about which class implements them.
+ // These can always be expected to return a valid Value*.
+ static Value* CreateNullValue();
+ static Value* CreateBooleanValue(bool in_value);
+ static Value* CreateIntegerValue(int in_value);
+ static Value* CreateRealValue(double in_value);
+ static Value* CreateStringValue(const std::wstring& in_value);
+
+ // This one can return NULL if the input isn't valid. If the return value
+ // is non-null, the new object has taken ownership of the buffer pointer.
+ static BinaryValue* CreateBinaryValue(char* buffer, size_t size);
+
+ typedef enum {
+ TYPE_NULL = 0,
+ TYPE_BOOLEAN,
+ TYPE_INTEGER,
+ TYPE_REAL,
+ TYPE_STRING,
+ TYPE_BINARY,
+ TYPE_DICTIONARY,
+ TYPE_LIST
+ } ValueType;
+
+ // Returns the type of the value stored by the current Value object.
+ // Each type will be implemented by only one subclass of Value, so it's
+ // safe to use the ValueType to determine whether you can cast from
+ // Value* to (Implementing Class)*. Also, a Value object never changes
+ // its type after construction.
+ ValueType GetType() const { return type_; }
+
+ // Returns true if the current object represents a given type.
+ bool IsType(ValueType type) const { return type == type_; }
+
+ // These methods allow the convenient retrieval of settings.
+ // If the current setting object can be converted into the given type,
+ // the value is returned through the "value" parameter and true is returned;
+ // otherwise, false is returned and "value" is unchanged.
+ virtual bool GetAsBoolean(bool* out_value) const;
+ virtual bool GetAsInteger(int* out_value) const;
+ virtual bool GetAsReal(double* out_value) const;
+ virtual bool GetAsString(std::wstring* out_value) const;
+
+ // This creates a deep copy of the entire Value tree, and returns a pointer
+ // to the copy. The caller gets ownership of the copy, of course.
+ virtual Value* DeepCopy() const;
+
+ // Compares if two Value objects have equal contents.
+ virtual bool Equals(const Value* other) const;
+
+ protected:
+ // This isn't safe for end-users (they should use the Create*Value()
+ // static methods above), but it's useful for subclasses.
+ Value(ValueType type) : type_(type) {}
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(Value);
+ Value();
+
+ ValueType type_;
+};
+
+// FundamentalValue represents the simple fundamental types of values.
+class FundamentalValue : public Value {
+ public:
+ FundamentalValue(bool in_value)
+ : Value(TYPE_BOOLEAN), boolean_value_(in_value) {}
+ FundamentalValue(int in_value)
+ : Value(TYPE_INTEGER), integer_value_(in_value) {}
+ FundamentalValue(double in_value)
+ : Value(TYPE_REAL), real_value_(in_value) {}
+ ~FundamentalValue();
+
+ // Subclassed methods
+ virtual bool GetAsBoolean(bool* out_value) const;
+ virtual bool GetAsInteger(int* out_value) const;
+ virtual bool GetAsReal(double* out_value) const;
+ virtual Value* DeepCopy() const;
+ virtual bool Equals(const Value* other) const;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(FundamentalValue);
+
+ union {
+ bool boolean_value_;
+ int integer_value_;
+ double real_value_;
+ };
+};
+
+class StringValue : public Value {
+ public:
+ StringValue(const std::wstring& in_value)
+ : Value(TYPE_STRING), value_(in_value) {}
+ ~StringValue();
+
+ // Subclassed methods
+ bool GetAsString(std::wstring* out_value) const;
+ Value* DeepCopy() const;
+ virtual bool Equals(const Value* other) const;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(StringValue);
+
+ std::wstring value_;
+};
+
+class BinaryValue: public Value {
+public:
+ // Creates a Value to represent a binary buffer. The new object takes
+ // ownership of the pointer passed in, if successful.
+ // Returns NULL if buffer is NULL.
+ static BinaryValue* Create(char* buffer, size_t size);
+
+ // For situations where you want to keep ownership of your buffer, this
+ // factory method creates a new BinaryValue by copying the contents of the
+ // buffer that's passed in.
+ // Returns NULL if buffer is NULL.
+ static BinaryValue* CreateWithCopiedBuffer(char* buffer, size_t size);
+
+ BinaryValue::~BinaryValue();
+
+ // Subclassed methods
+ Value* DeepCopy() const;
+ virtual bool Equals(const Value* other) const;
+
+ size_t GetSize() const { return size_; }
+ char* GetBuffer() { return buffer_; }
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(BinaryValue);
+
+ // Constructor is private so that only objects with valid buffer pointers
+ // and size values can be created.
+ BinaryValue::BinaryValue(char* buffer, size_t size);
+
+ char* buffer_;
+ size_t size_;
+};
+
+class DictionaryValue : public Value {
+ public:
+ DictionaryValue() : Value(TYPE_DICTIONARY) {}
+ ~DictionaryValue();
+
+ // Subclassed methods
+ Value* DeepCopy() const;
+ virtual bool Equals(const Value* other) const;
+
+ // Returns true if the current dictionary has a value for the given key.
+ bool DictionaryValue::HasKey(const std::wstring& key);
+
+ // Clears any current contents of this dictionary.
+ void DictionaryValue::Clear();
+
+ // Sets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. Obviously, "." can't be used
+ // within a key, but there are no other restrictions on keys.
+ // If the key at any step of the way doesn't exist, or exists but isn't
+ // a DictionaryValue, a new DictionaryValue will be created and attached
+ // to the path in that location.
+ // Note that the dictionary takes ownership of the value
+ // referenced by in_value.
+ bool Set(const std::wstring& path, Value* in_value);
+
+ // Convenience forms of Set(). These methods will replace any existing
+ // value at that path, even if it has a different type.
+ bool SetBoolean(const std::wstring& path, bool in_value);
+ bool SetInteger(const std::wstring& path, int in_value);
+ bool SetReal(const std::wstring& path, double in_value);
+ bool SetString(const std::wstring& path, const std::wstring& in_value);
+
+ // Gets the Value associated with the given path starting from this object.
+ // A path has the form "<key>" or "<key>.<key>.[...]", where "." indexes
+ // into the next DictionaryValue down. If the path can be resolved
+ // successfully, the value for the last key in the path will be returned
+ // through the "value" parameter, and the function will return true.
+ // Otherwise, it will return false and "value" will be untouched.
+ // Note that the dictionary always owns the value that's returned.
+ bool Get(const std::wstring& path, Value** out_value) const;
+
+ // These are convenience forms of Get(). The value will be retrieved
+ // and the return value will be true if the path is valid and the value at
+ // the end of the path can be returned in the form specified.
+ bool GetBoolean(const std::wstring& path, bool* out_value) const;
+ bool GetInteger(const std::wstring& path, int* out_value) const;
+ bool GetReal(const std::wstring& path, double* out_value) const;
+ bool GetString(const std::wstring& path, std::wstring* out_value) const;
+ bool GetBinary(const std::wstring& path, BinaryValue** out_value) const;
+ bool GetDictionary(const std::wstring& path,
+ DictionaryValue** out_value) const;
+ bool GetList(const std::wstring& path, ListValue** out_value) const;
+
+ // Removes the Value with the specified path from this dictionary (or one
+ // of its child dictionaries, if the path is more than just a local key).
+ // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+ // passed out via out_value. If |out_value| is NULL, the removed value will
+ // be deleted. This method returns true if |path| is a valid path; otherwise
+ // it will return false and the DictionaryValue object will be unchanged.
+ bool Remove(const std::wstring& path, Value** out_value);
+
+ // This class provides an iterator for the keys in the dictionary.
+ // It can't be used to modify the dictionary.
+ class key_iterator
+ : private std::iterator<std::input_iterator_tag, const std::wstring> {
+ public:
+ key_iterator(ValueMap::const_iterator itr) { itr_ = itr; }
+ key_iterator operator++() { ++itr_; return *this; }
+ const std::wstring& operator*() { return itr_->first; }
+ bool operator!=(const key_iterator& other) { return itr_ != other.itr_; }
+ bool operator==(const key_iterator& other) { return itr_ == other.itr_; }
+
+ private:
+ ValueMap::const_iterator itr_;
+ };
+
+ key_iterator begin_keys() const { return key_iterator(dictionary_.begin()); }
+ key_iterator end_keys() const { return key_iterator(dictionary_.end()); }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(DictionaryValue);
+
+ // Associates the value |in_value| with the |key|. This method should be
+ // used instead of "dictionary_[key] = foo" so that any previous value can
+ // be properly deleted.
+ void SetInCurrentNode(const std::wstring& key, Value* in_value);
+
+ ValueMap dictionary_;
+};
+
+// This type of Value represents a list of other Value values.
+// TODO(jhughes): Flesh this out.
+class ListValue : public Value {
+ public:
+ ListValue() : Value(TYPE_LIST) {}
+ ~ListValue();
+
+ // Subclassed methods
+ Value* DeepCopy() const;
+ virtual bool Equals(const Value* other) const;
+
+ // Clears the contents of this ListValue
+ void Clear();
+
+ // Returns the number of Values in this list.
+ size_t GetSize() const { return list_.size(); }
+
+ // Sets the list item at the given index to be the Value specified by
+ // the value given. If the index beyond the current end of the list, null
+ // Values will be used to pad out the list.
+ // Returns true if successful, or false if the index was negative or
+ // the value is a null pointer.
+ bool Set(size_t index, Value* in_value);
+
+ // Gets the Value at the given index. Modifies value (and returns true)
+ // only if the index falls within the current list range.
+ // Note that the list always owns the Value passed out via out_value.
+ bool Get(size_t index, Value** out_value) const;
+
+ // Convenience forms of Get(). Modifies value (and returns true) only if
+ // the index is valid and the Value at that index can be returned in
+ // the specified form.
+ bool GetDictionary(size_t index, DictionaryValue** out_value) const;
+
+ // Removes the Value with the specified index from this list.
+ // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be
+ // passed out via out_value. If |out_value| is NULL, the removed value will
+ // be deleted. This method returns true if |index| is valid; otherwise
+ // it will return false and the ListValue object will be unchanged.
+ bool Remove(size_t index, Value** out_value);
+
+ // Appends a Value to the end of the list.
+ void Append(Value* in_value);
+
+ // Iteration
+ typedef ValueVector::iterator iterator;
+ typedef ValueVector::const_iterator const_iterator;
+
+ ListValue::iterator begin() { return list_.begin(); }
+ ListValue::iterator end() { return list_.end(); }
+
+ ListValue::const_iterator begin() const { return list_.begin(); }
+ ListValue::const_iterator end() const { return list_.end(); }
+
+ ListValue::iterator Erase(iterator item) {
+ return list_.erase(item);
+ }
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(ListValue);
+
+ ValueVector list_;
+};
+
+// This interface is implemented by classes that know how to serialize and
+// deserialize Value objects.
+class ValueSerializer {
+ public:
+ virtual bool Serialize(const Value& root) = 0;
+
+ // This method deserializes the subclass-specific format into a Value object.
+ // The method should return true if and only if the root parameter is set
+ // to a complete Value representation of the serialized form. If the
+ // return value is true, the caller takes ownership of the objects pointed
+ // to by root. If the return value is false, root should be unchanged.
+ virtual bool Deserialize(Value** root) = 0;
+};
+
+#endif // CHROME_COMMON_VALUES_H__
diff --git a/base/values_unittest.cc b/base/values_unittest.cc
new file mode 100644
index 0000000..1470a0d
--- /dev/null
+++ b/base/values_unittest.cc
@@ -0,0 +1,403 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/values.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class ValuesTest: public testing::Test {
+};
+
+TEST(ValuesTest, Basic) {
+ // Test basic dictionary getting/setting
+ DictionaryValue settings;
+ std::wstring homepage = L"http://google.com";
+ ASSERT_FALSE(
+ settings.GetString(L"global.homepage", &homepage));
+ ASSERT_EQ(std::wstring(L"http://google.com"), homepage);
+
+ ASSERT_FALSE(settings.Get(L"global", NULL));
+ ASSERT_TRUE(settings.Set(L"global", Value::CreateBooleanValue(true)));
+ ASSERT_TRUE(settings.Get(L"global", NULL));
+ ASSERT_TRUE(settings.SetString(L"global.homepage", L"http://scurvy.com"));
+ ASSERT_TRUE(settings.Get(L"global", NULL));
+ homepage = L"http://google.com";
+ ASSERT_TRUE(settings.GetString(L"global.homepage", &homepage));
+ ASSERT_EQ(std::wstring(L"http://scurvy.com"), homepage);
+
+ // Test storing a dictionary in a list.
+ ListValue* toolbar_bookmarks;
+ ASSERT_FALSE(
+ settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks));
+
+ toolbar_bookmarks = new ListValue;
+ settings.Set(L"global.toolbar.bookmarks", toolbar_bookmarks);
+ ASSERT_TRUE(
+ settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks));
+
+ DictionaryValue* new_bookmark = new DictionaryValue;
+ new_bookmark->SetString(L"name", L"Froogle");
+ new_bookmark->SetString(L"url", L"http://froogle.com");
+ toolbar_bookmarks->Append(new_bookmark);
+
+ ListValue* bookmark_list;
+ ASSERT_TRUE(settings.GetList(L"global.toolbar.bookmarks", &bookmark_list));
+ DictionaryValue* bookmark;
+ ASSERT_EQ(1, bookmark_list->GetSize());
+ ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark));
+ std::wstring bookmark_name = L"Unnamed";
+ ASSERT_TRUE(bookmark->GetString(L"name", &bookmark_name));
+ ASSERT_EQ(std::wstring(L"Froogle"), bookmark_name);
+ std::wstring bookmark_url;
+ ASSERT_TRUE(bookmark->GetString(L"url", &bookmark_url));
+ ASSERT_EQ(std::wstring(L"http://froogle.com"), bookmark_url);
+}
+
+TEST(ValuesTest, BinaryValue) {
+ char* buffer = NULL;
+ // Passing a null buffer pointer doesn't yield a BinaryValue
+ BinaryValue* binary = BinaryValue::Create(buffer, 0);
+ ASSERT_FALSE(binary);
+
+ // If you want to represent an empty binary value, use a zero-length buffer.
+ buffer = new char[0];
+ ASSERT_TRUE(buffer);
+ binary = BinaryValue::Create(buffer, 0);
+ ASSERT_TRUE(binary);
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_EQ(buffer, binary->GetBuffer());
+ ASSERT_EQ(0, binary->GetSize());
+ delete binary;
+ binary = NULL;
+
+ // Test the common case of a non-empty buffer
+ buffer = new char[15];
+ binary = BinaryValue::Create(buffer, 15);
+ ASSERT_TRUE(binary);
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_EQ(buffer, binary->GetBuffer());
+ ASSERT_EQ(15, binary->GetSize());
+ delete binary;
+ binary = NULL;
+
+ char stack_buffer[42];
+ memset(stack_buffer, '!', 42);
+ binary = BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42);
+ ASSERT_TRUE(binary);
+ ASSERT_TRUE(binary->GetBuffer());
+ ASSERT_NE(stack_buffer, binary->GetBuffer());
+ ASSERT_EQ(42, binary->GetSize());
+ ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize()));
+ delete binary;
+}
+
+// This is a Value object that allows us to tell if it's been
+// properly deleted by modifying the value of external flag on destruction.
+class DeletionTestValue : public Value {
+public:
+ DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+ Init(deletion_flag); // Separate function so that we can use ASSERT_*
+ }
+
+ void Init(bool* deletion_flag) {
+ ASSERT_TRUE(deletion_flag);
+ deletion_flag_ = deletion_flag;
+ *deletion_flag_ = false;
+ }
+
+ ~DeletionTestValue() {
+ *deletion_flag_ = true;
+ }
+
+private:
+ bool* deletion_flag_;
+};
+
+TEST(ValuesTest, ListDeletion) {
+ bool deletion_flag = true;
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ list.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
+ EXPECT_TRUE(deletion_flag);
+ }
+}
+
+TEST(ValuesTest, ListRemoval) {
+ bool deletion_flag = true;
+ Value* removed_item = NULL;
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_EQ(1, list.GetSize());
+ EXPECT_FALSE(list.Remove(-1, &removed_item));
+ EXPECT_FALSE(list.Remove(1, &removed_item));
+ EXPECT_TRUE(list.Remove(0, &removed_item));
+ ASSERT_TRUE(removed_item);
+ EXPECT_EQ(0, list.GetSize());
+ }
+ EXPECT_FALSE(deletion_flag);
+ delete removed_item;
+ removed_item = NULL;
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ ListValue list;
+ list.Append(new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(list.Remove(0, NULL));
+ EXPECT_TRUE(deletion_flag);
+ EXPECT_EQ(0, list.GetSize());
+ }
+}
+
+TEST(ValuesTest, DictionaryDeletion) {
+ std::wstring key = L"test";
+ bool deletion_flag = true;
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ }
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ dict.Clear();
+ EXPECT_TRUE(deletion_flag);
+ }
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ dict.Set(key, Value::CreateNullValue());
+ EXPECT_TRUE(deletion_flag);
+ }
+}
+
+TEST(ValuesTest, DictionaryRemoval) {
+ std::wstring key = L"test";
+ bool deletion_flag = true;
+ Value* removed_item = NULL;
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(dict.HasKey(key));
+ EXPECT_FALSE(dict.Remove(L"absent key", &removed_item));
+ EXPECT_TRUE(dict.Remove(key, &removed_item));
+ EXPECT_FALSE(dict.HasKey(key));
+ ASSERT_TRUE(removed_item);
+ }
+ EXPECT_FALSE(deletion_flag);
+ delete removed_item;
+ removed_item = NULL;
+ EXPECT_TRUE(deletion_flag);
+
+ {
+ DictionaryValue dict;
+ dict.Set(key, new DeletionTestValue(&deletion_flag));
+ EXPECT_FALSE(deletion_flag);
+ EXPECT_TRUE(dict.HasKey(key));
+ EXPECT_TRUE(dict.Remove(key, NULL));
+ EXPECT_TRUE(deletion_flag);
+ EXPECT_FALSE(dict.HasKey(key));
+ }
+}
+
+TEST(ValuesTest, DeepCopy) {
+ DictionaryValue original_dict;
+ Value* original_null = Value::CreateNullValue();
+ original_dict.Set(L"null", original_null);
+ Value* original_bool = Value::CreateBooleanValue(true);
+ original_dict.Set(L"bool", original_bool);
+ Value* original_int = Value::CreateIntegerValue(42);
+ original_dict.Set(L"int", original_int);
+ Value* original_real = Value::CreateRealValue(3.14);
+ original_dict.Set(L"real", original_real);
+ Value* original_string = Value::CreateStringValue(L"peek-a-boo");
+ original_dict.Set(L"string", original_string);
+
+ char* original_buffer = new char[42];
+ memset(original_buffer, '!', 42);
+ BinaryValue* original_binary = Value::CreateBinaryValue(original_buffer, 42);
+ original_dict.Set(L"binary", original_binary);
+
+ ListValue* original_list = new ListValue();
+ Value* original_list_element_0 = Value::CreateIntegerValue(0);
+ original_list->Append(original_list_element_0);
+ Value* original_list_element_1 = Value::CreateIntegerValue(1);
+ original_list->Append(original_list_element_1);
+ original_dict.Set(L"list", original_list);
+
+ DictionaryValue* copy_dict =
+ static_cast<DictionaryValue*>(original_dict.DeepCopy());
+ ASSERT_TRUE(copy_dict);
+ ASSERT_NE(copy_dict, &original_dict);
+
+ Value* copy_null = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"null", &copy_null));
+ ASSERT_TRUE(copy_null);
+ ASSERT_NE(copy_null, original_null);
+ ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
+
+ Value* copy_bool = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"bool", &copy_bool));
+ ASSERT_TRUE(copy_bool);
+ ASSERT_NE(copy_bool, original_bool);
+ ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
+ bool copy_bool_value = false;
+ ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
+ ASSERT_TRUE(copy_bool_value);
+
+ Value* copy_int = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"int", &copy_int));
+ ASSERT_TRUE(copy_int);
+ ASSERT_NE(copy_int, original_int);
+ ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
+ int copy_int_value = 0;
+ ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
+ ASSERT_EQ(42, copy_int_value);
+
+ Value* copy_real = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"real", &copy_real));
+ ASSERT_TRUE(copy_real);
+ ASSERT_NE(copy_real, original_real);
+ ASSERT_TRUE(copy_real->IsType(Value::TYPE_REAL));
+ double copy_real_value = 0;
+ ASSERT_TRUE(copy_real->GetAsReal(&copy_real_value));
+ ASSERT_EQ(3.14, copy_real_value);
+
+ Value* copy_string = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"string", &copy_string));
+ ASSERT_TRUE(copy_string);
+ ASSERT_NE(copy_string, original_string);
+ ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
+ std::wstring copy_string_value;
+ ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
+ ASSERT_EQ(std::wstring(L"peek-a-boo"), copy_string_value);
+
+ Value* copy_binary = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"binary", &copy_binary));
+ ASSERT_TRUE(copy_binary);
+ ASSERT_NE(copy_binary, original_binary);
+ ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+ ASSERT_NE(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer());
+ ASSERT_EQ(original_binary->GetSize(),
+ static_cast<BinaryValue*>(copy_binary)->GetSize());
+ ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
+ static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
+ original_binary->GetSize()));
+
+ Value* copy_value = NULL;
+ ASSERT_TRUE(copy_dict->Get(L"list", &copy_value));
+ ASSERT_TRUE(copy_value);
+ ASSERT_NE(copy_value, original_list);
+ ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
+ ListValue* copy_list = static_cast<ListValue*>(copy_value);
+ ASSERT_EQ(2, copy_list->GetSize());
+
+ Value* copy_list_element_0;
+ ASSERT_TRUE(copy_list->Get(0, &copy_list_element_0));
+ ASSERT_TRUE(copy_list_element_0);
+ ASSERT_NE(copy_list_element_0, original_list_element_0);
+ int copy_list_element_0_value;
+ ASSERT_TRUE(copy_list_element_0->GetAsInteger(&copy_list_element_0_value));
+ ASSERT_EQ(0, copy_list_element_0_value);
+
+ Value* copy_list_element_1;
+ ASSERT_TRUE(copy_list->Get(1, &copy_list_element_1));
+ ASSERT_TRUE(copy_list_element_1);
+ ASSERT_NE(copy_list_element_1, original_list_element_1);
+ int copy_list_element_1_value;
+ ASSERT_TRUE(copy_list_element_1->GetAsInteger(&copy_list_element_1_value));
+ ASSERT_EQ(1, copy_list_element_1_value);
+
+ delete copy_dict;
+}
+
+TEST(ValuesTest, Equals) {
+ Value* null1 = Value::CreateNullValue();
+ Value* null2 = Value::CreateNullValue();
+ EXPECT_NE(null1, null2);
+ EXPECT_TRUE(null1->Equals(null2));
+
+ Value* boolean = Value::CreateBooleanValue(false);
+ EXPECT_FALSE(null1->Equals(boolean));
+ delete null1;
+ delete null2;
+ delete boolean;
+
+ DictionaryValue dv;
+ dv.SetBoolean(L"a", false);
+ dv.SetInteger(L"b", 2);
+ dv.SetReal(L"c", 2.5);
+ dv.SetString(L"d", L"string");
+ dv.Set(L"e", Value::CreateNullValue());
+
+ DictionaryValue* copy = static_cast<DictionaryValue*>(dv.DeepCopy());
+ EXPECT_TRUE(dv.Equals(copy));
+
+ ListValue* list = new ListValue;
+ list->Append(Value::CreateNullValue());
+ list->Append(new DictionaryValue);
+ dv.Set(L"f", list);
+
+ EXPECT_FALSE(dv.Equals(copy));
+ copy->Set(L"f", list->DeepCopy());
+ EXPECT_TRUE(dv.Equals(copy));
+
+ list->Append(Value::CreateBooleanValue(true));
+ EXPECT_FALSE(dv.Equals(copy));
+ delete copy;
+}
diff --git a/base/watchdog.cc b/base/watchdog.cc
new file mode 100644
index 0000000..1d9c183
--- /dev/null
+++ b/base/watchdog.cc
@@ -0,0 +1,171 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/watchdog.h"
+#include "base/string_util.h"
+#include "base/thread.h"
+
+//------------------------------------------------------------------------------
+// Public API methods.
+
+// Start thread running in a Disarmed state.
+Watchdog::Watchdog(const TimeDelta& duration,
+ const std::wstring& thread_watched_name,
+ bool enabled)
+ : lock_(),
+ condition_variable_(&lock_),
+ state_(DISARMED),
+ duration_(duration),
+ thread_watched_name_(thread_watched_name),
+ handle_(NULL),
+ thread_id_(0) {
+ if (!enabled)
+ return; // Don't start thread, or doing anything really.
+ handle_ = CreateThread(NULL, // security
+ 0, // Default stack size.
+ Watchdog::ThreadStart,
+ reinterpret_cast<void*>(this),
+ CREATE_SUSPENDED,
+ &thread_id_);
+ DCHECK(NULL != handle_);
+ if (NULL == handle_)
+ return ;
+ ResumeThread(handle_); // WINAPI call.
+}
+
+// Notify watchdog thread, and wait for it to finish up.
+Watchdog::~Watchdog() {
+ if (NULL == handle_)
+ return;
+ {
+ AutoLock lock(lock_);
+ state_ = SHUTDOWN;
+ }
+ condition_variable_.Signal();
+ DWORD results = WaitForSingleObject(handle_, INFINITE);
+ DCHECK(WAIT_OBJECT_0 == results);
+ CloseHandle(handle_);
+ handle_ = NULL;
+}
+
+void Watchdog::Arm() {
+ ArmAtStartTime(TimeTicks::Now());
+}
+
+void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) {
+ ArmAtStartTime(TimeTicks::Now() - time_delta);
+}
+
+// Start clock for watchdog.
+void Watchdog::ArmAtStartTime(const TimeTicks start_time) {
+ {
+ AutoLock lock(lock_);
+ start_time_ = start_time;
+ state_ = ARMED;
+ }
+ // Force watchdog to wake up, and go to sleep with the timer ticking with the
+ // proper duration.
+ condition_variable_.Signal();
+}
+
+// Disable watchdog so that it won't do anything when time expires.
+void Watchdog::Disarm() {
+ if (NULL == handle_)
+ return;
+ AutoLock lock(lock_);
+ state_ = DISARMED;
+ // We don't need to signal, as the watchdog will eventually wake up, and it
+ // will check its state and time, and act accordingly.
+}
+
+//------------------------------------------------------------------------------
+// Internal private methods that the watchdog thread uses.
+
+// static
+DWORD __stdcall Watchdog::ThreadStart(void* pThis) {
+ Watchdog* watchdog = reinterpret_cast<Watchdog*>(pThis);
+ return watchdog->Run();
+}
+
+unsigned Watchdog::Run() {
+ SetThreadName();
+ TimeDelta remaining_duration;
+ while (1) {
+ AutoLock lock(lock_);
+ while (DISARMED == state_)
+ condition_variable_.Wait();
+ if (SHUTDOWN == state_)
+ return 0;
+ DCHECK(ARMED == state_);
+ remaining_duration = duration_ - (TimeTicks::Now() - start_time_);
+ if (remaining_duration.InMilliseconds() > 0) {
+ // Spurios wake? Timer drifts? Go back to sleep for remaining time.
+ condition_variable_.TimedWait(remaining_duration);
+ } else {
+ // 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(static_lock_);
+ if (last_debugged_alarm_time_ > start_time_) {
+ // False alarm: we started our clock before the debugger break (last
+ // alarm time).
+ start_time_ += last_debugged_alarm_delay_;
+ if (last_debugged_alarm_time_ > start_time_)
+ state_ = DISARMED; // Too many alarms must have taken place.
+ continue;
+ }
+ }
+ state_ = DISARMED; // Only alarm at most once.
+ TimeTicks last_alarm_time = TimeTicks::Now();
+ Alarm(); // Set a break point here to debug on alarms.
+ TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time;
+ if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) {
+ // Ignore race of two alarms/breaks going off at roughly the same time.
+ AutoLock static_lock(static_lock_);
+ // This was a real debugger break.
+ last_debugged_alarm_time_ = last_alarm_time;
+ last_debugged_alarm_delay_ = last_alarm_delay;
+ }
+ }
+ }
+}
+
+void Watchdog::SetThreadName() const {
+ std::string name = StringPrintf("%s Watchdog",
+ WideToASCII(thread_watched_name_).c_str());
+ Thread::SetThreadName(name.c_str(), thread_id_);
+ DLOG(INFO) << "Watchdog active: " << name;
+}
+
+// static
+Lock Watchdog::static_lock_; // Lock for access of static data...
+// static
+TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks();
+// static
+TimeDelta Watchdog::last_debugged_alarm_delay_;
diff --git a/base/watchdog.h b/base/watchdog.h
new file mode 100644
index 0000000..2871941
--- /dev/null
+++ b/base/watchdog.h
@@ -0,0 +1,111 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The Watchdog class creates a second thread that can Alarm if a specific
+// duration of time passes without proper attention. The duration of time is
+// specified at construction time. The Watchdog may be used many times by
+// simply calling Arm() (to start timing) and Disarm() (to reset the timer).
+// The Watchdog is typically used under a debugger, where the stack traces on
+// other threads can be examined if/when the Watchdog alarms.
+
+// Some watchdogs will be enabled or disabled via command line switches. To
+// facilitate such code, an "enabled" argument for the constuctor can be used
+// to permanently disable the watchdog. Disabled watchdogs don't even spawn
+// a second thread, and their methods call (Arm() and Disarm()) return very
+// quickly.
+
+#ifndef BASE_WATCHDOG_H__
+#define BASE_WATCHDOG_H__
+
+#include <string>
+
+#include "base/condition_variable.h"
+#include "base/lock.h"
+#include "base/time.h"
+
+class Watchdog {
+ public:
+ // TODO(JAR)change default arg to required arg after all users have migrated.
+ // Constructor specifies how long the Watchdog will wait before alarming.
+ Watchdog(const TimeDelta& duration,
+ const std::wstring& thread_watched_name,
+ bool enabled = true);
+ virtual ~Watchdog();
+
+ // Start timing, and alarm when time expires (unless we're disarm()ed.)
+ void Arm(); // Arm starting now.
+ void ArmSomeTimeDeltaAgo(const TimeDelta& time_delta);
+ void ArmAtStartTime(const TimeTicks start_time);
+
+ // Reset time, and do not set off the alarm.
+ void Disarm();
+
+ // Alarm is called if the time expires after an Arm() without someone calling
+ // Disarm(). This method can be overridden to create testable classes.
+ virtual void Alarm() {
+ DLOG(INFO) << "Watchdog alarmed for " << thread_watched_name_;
+ }
+
+ private:
+ enum State {ARMED, DISARMED, SHUTDOWN };
+
+ // Windows thread start callback
+ static DWORD WINAPI ThreadStart(void* pThis);
+
+ // Loop and test function for our watchdog thread.
+ unsigned Run();
+ void Watchdog::SetThreadName() const;
+
+ Lock lock_; // Mutex for state_.
+ ConditionVariable condition_variable_;
+ State state_;
+ const TimeDelta duration_; // How long after start_time_ do we alarm?
+ const std::wstring thread_watched_name_;
+ HANDLE handle_; // Handle for watchdog thread.
+ DWORD thread_id_; // Also for watchdog thread.
+
+ TimeTicks start_time_; // Start of epoch, and alarm after duration_.
+
+ // When the debugger breaks (when we alarm), all the other alarms that are
+ // armed will expire (also alarm). To diminish this effect, we track any
+ // delay due to debugger breaks, and we *try* to adjust the effective start
+ // time of other alarms to step past the debugging break.
+ // Without this safety net, any alarm will typically trigger a host of follow
+ // on alarms from callers that specify old times.
+ static Lock static_lock_; // Lock for access of static data...
+ // When did we last alarm and get stuck (for a while) in a debugger?
+ static TimeTicks last_debugged_alarm_time_;
+ // How long did we sit on a break in the debugger?
+ static TimeDelta last_debugged_alarm_delay_;
+
+
+ DISALLOW_EVIL_CONSTRUCTORS(Watchdog);
+};
+
+#endif // BASE_WATCHDOG_H__
diff --git a/base/watchdog_test.cc b/base/watchdog_test.cc
new file mode 100644
index 0000000..3f569af
--- /dev/null
+++ b/base/watchdog_test.cc
@@ -0,0 +1,153 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests for Watchdog class.
+
+#include "base/logging.h"
+#include "base/watchdog.h"
+#include "base/spin_wait.h"
+#include "base/time.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+//------------------------------------------------------------------------------
+// Provide a derived class to facilitate testing.
+
+// TODO(JAR): Remove default argument from constructor, and make mandatory.
+class WatchdogCounter : public Watchdog {
+ public:
+ WatchdogCounter(const TimeDelta& duration,
+ const std::wstring& thread_watched_name,
+ bool enabled = true)
+ : Watchdog(duration, thread_watched_name, enabled), alarm_counter_(0) {
+ }
+
+ virtual ~WatchdogCounter() {}
+
+ virtual void Alarm() {
+ alarm_counter_++;
+ Watchdog::Alarm();
+ }
+
+ int alarm_counter() { return alarm_counter_; }
+
+ private:
+ int alarm_counter_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WatchdogCounter);
+};
+
+class WatchdogTest : public testing::Test {
+};
+
+
+//------------------------------------------------------------------------------
+// Actual tests
+
+// Minimal constructor/destructor test.
+TEST(WatchdogTest, StartupShutdownTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), L"Disabled", false);
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), L"Enabled", true);
+
+ // The following test is depricated, and should be removed when the
+ // default argument constructor is no longer accepted.
+ Watchdog watchdog3(TimeDelta::FromMilliseconds(300), L"Default");
+}
+
+// Test ability to call Arm and Disarm repeatedly.
+TEST(WatchdogTest, ArmDisarmTest) {
+ Watchdog watchdog1(TimeDelta::FromMilliseconds(300), L"Disabled", false);
+ watchdog1.Arm();
+ watchdog1.Disarm();
+ watchdog1.Arm();
+ watchdog1.Disarm();
+
+ Watchdog watchdog2(TimeDelta::FromMilliseconds(300), L"Enabled", true);
+ watchdog2.Arm();
+ watchdog2.Disarm();
+ watchdog2.Arm();
+ watchdog2.Disarm();
+
+ // The following test is depricated, and should be removed when the
+ // default argument constructor is no longer accepted.
+ Watchdog watchdog3(TimeDelta::FromMilliseconds(300), L"Default");
+ watchdog3.Arm();
+ watchdog3.Disarm();
+ watchdog3.Arm();
+ watchdog3.Disarm();
+}
+
+// Make sure a basic alarm fires when the time has expired.
+TEST(WatchdogTest, AlarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), L"Enabled", true);
+ watchdog.Arm();
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+ watchdog.alarm_counter() > 0);
+ EXPECT_EQ(1, watchdog.alarm_counter());
+
+ // Set a time greater than the timeout into the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+ // It should instantly go off, but certainly in less than a second.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+ watchdog.alarm_counter() > 1);
+
+ EXPECT_EQ(2, watchdog.alarm_counter());
+}
+
+// Make sure a disable alarm does nothing, even if we arm it.
+TEST(WatchdogTest, ConstructorDisabledTest) {
+ WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), L"Disabled", false);
+ watchdog.Arm();
+ // Alarm should not fire, as it was disabled.
+ Sleep(500);
+ EXPECT_EQ(0, watchdog.alarm_counter());
+}
+
+// Make sure Disarming will prevent firing, even after Arming.
+TEST(WatchdogTest, DisarmTest) {
+ WatchdogCounter watchdog(TimeDelta::FromSeconds(1), L"Enabled", true);
+ watchdog.Arm();
+ Sleep(100); // Don't sleep too long
+ watchdog.Disarm();
+ // Alarm should not fire.
+ Sleep(1500);
+ EXPECT_EQ(0, watchdog.alarm_counter());
+
+ // ...but even after disarming, we can still use the alarm...
+ // Set a time greater than the timeout into the past.
+ watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2));
+ // It should almost instantly go off, but certainly in less than a second.
+ SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromSeconds(1),
+ watchdog.alarm_counter() > 0);
+
+ EXPECT_EQ(1, watchdog.alarm_counter());
+}
+
+} // namespace
diff --git a/base/win_util.cc b/base/win_util.cc
new file mode 100644
index 0000000..478a5a6
--- /dev/null
+++ b/base/win_util.cc
@@ -0,0 +1,359 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/win_util.h"
+
+#include <sddl.h>
+
+#include "base/logging.h"
+#include "base/registry.h"
+#include "base/scoped_handle.h"
+#include "base/string_util.h"
+
+namespace win_util {
+
+#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(struct_name, member) \
+ offsetof(struct_name, member) + \
+ (sizeof static_cast<struct_name*>(NULL)->member)
+#define NONCLIENTMETRICS_SIZE_PRE_VISTA \
+ SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont)
+
+void GetNonClientMetrics(NONCLIENTMETRICS* metrics) {
+ DCHECK(metrics);
+
+ static const UINT SIZEOF_NONCLIENTMETRICS =
+ (GetWinVersion() == WINVERSION_VISTA) ?
+ sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA;
+ metrics->cbSize = SIZEOF_NONCLIENTMETRICS;
+ const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
+ SIZEOF_NONCLIENTMETRICS, metrics,
+ 0);
+ DCHECK(success);
+}
+
+WinVersion GetWinVersion() {
+ static bool checked_version = false;
+ static WinVersion win_version = WINVERSION_PRE_2000;
+ if (!checked_version) {
+ OSVERSIONINFO version_info;
+ version_info.dwOSVersionInfoSize = sizeof version_info;
+ GetVersionEx(&version_info);
+ if (version_info.dwMajorVersion == 5) {
+ switch (version_info.dwMinorVersion) {
+ case 0:
+ win_version = WINVERSION_2000;
+ break;
+ case 1:
+ win_version = WINVERSION_XP;
+ break;
+ case 2:
+ default:
+ win_version = WINVERSION_SERVER_2003;
+ break;
+ }
+ } else if (version_info.dwMajorVersion >= 6) {
+ win_version = WINVERSION_VISTA;
+ }
+ checked_version = true;
+ }
+ return win_version;
+}
+
+bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid,
+ ACCESS_MASK access) {
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ PACL old_dacl = NULL;
+ PACL new_dacl = NULL;
+
+ if (ERROR_SUCCESS != GetSecurityInfo(handle, SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, NULL,
+ &descriptor))
+ return false;
+
+ BYTE sid[SECURITY_MAX_SID_SIZE] = {0};
+ DWORD size_sid = SECURITY_MAX_SID_SIZE;
+
+ if (known_sid == WinSelfSid) {
+ // We hijack WinSelfSid when we want to add the current user instead of
+ // a known sid.
+ HANDLE token = NULL;
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
+ LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD size = sizeof(TOKEN_USER) + size_sid;
+ TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
+ scoped_ptr<TOKEN_USER> token_user_ptr(token_user);
+ BOOL ret = GetTokenInformation(token, TokenUser, token_user, size, &size);
+
+ CloseHandle(token);
+
+ if (!ret) {
+ LocalFree(descriptor);
+ return false;
+ }
+ memcpy(sid, token_user->User.Sid, size_sid);
+ } else {
+ if (!CreateWellKnownSid(known_sid , NULL, sid, &size_sid)) {
+ LocalFree(descriptor);
+ return false;
+ }
+ }
+
+ EXPLICIT_ACCESS new_access = {0};
+ new_access.grfAccessMode = GRANT_ACCESS;
+ new_access.grfAccessPermissions = access;
+ new_access.grfInheritance = NO_INHERITANCE;
+
+ new_access.Trustee.pMultipleTrustee = NULL;
+ new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
+ new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
+ new_access.Trustee.ptstrName = reinterpret_cast<LPWSTR>(&sid);
+
+ if (ERROR_SUCCESS != SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl)) {
+ LocalFree(descriptor);
+ return false;
+ }
+
+ DWORD result = SetSecurityInfo(handle, SE_KERNEL_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL,
+ new_dacl, NULL);
+
+ LocalFree(new_dacl);
+ LocalFree(descriptor);
+
+ if (ERROR_SUCCESS != result)
+ return false;
+
+ return true;
+}
+
+bool GetUserSidString(std::wstring* user_sid) {
+ // Get the current token.
+ HANDLE token = NULL;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+ return false;
+ ScopedHandle token_scoped(token);
+
+ DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
+ scoped_ptr<TOKEN_USER> user(reinterpret_cast<TOKEN_USER*>(new BYTE[size]));
+
+ if (!::GetTokenInformation(token, TokenUser, user.get(), size, &size))
+ return false;
+
+ if (!user->User.Sid)
+ return false;
+
+ // Convert the data to a string.
+ wchar_t* sid_string;
+ if (!::ConvertSidToStringSid(user->User.Sid, &sid_string))
+ return false;
+
+ *user_sid = sid_string;
+
+ ::LocalFree(sid_string);
+
+ return true;
+}
+
+bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor) {
+ // Get the current token.
+ HANDLE token = NULL;
+ if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token))
+ return false;
+ ScopedHandle token_scoped(token);
+
+ // Get the size of the TokenGroups structure.
+ DWORD size = 0;
+ BOOL result = GetTokenInformation(token, TokenGroups, NULL, 0, &size);
+ if (result != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ return false;
+
+ // Get the data.
+ scoped_ptr<TOKEN_GROUPS> token_groups;
+ token_groups.reset(reinterpret_cast<TOKEN_GROUPS*>(new char[size]));
+
+ if (!GetTokenInformation(token, TokenGroups, token_groups.get(), size, &size))
+ return false;
+
+ // Look for the logon sid.
+ SID* logon_sid = NULL;
+ for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
+ if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
+ logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
+ break;
+ }
+ }
+
+ if (!logon_sid)
+ return false;
+
+ // Convert the data to a string.
+ wchar_t* sid_string;
+ if (!ConvertSidToStringSid(logon_sid, &sid_string))
+ return false;
+
+ static const wchar_t dacl_format[] = L"D:(A;OICI;GA;;;%s)";
+ wchar_t dacl[SECURITY_MAX_SID_SIZE + arraysize(dacl_format) + 1] = {0};
+ wsprintf(dacl, dacl_format, sid_string);
+
+ LocalFree(sid_string);
+
+ // Convert the string to a security descriptor
+ if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+ dacl,
+ SDDL_REVISION_1,
+ reinterpret_cast<PSECURITY_DESCRIPTOR*>(security_descriptor),
+ NULL)) {
+ return false;
+ }
+
+ return true;
+}
+
+#pragma warning(push)
+#pragma warning(disable:4312 4244)
+WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) {
+ // The reason we don't return the SetwindowLongPtr() value is that it returns
+ // the orignal window procedure and not the current one. I don't know if it is
+ // a bug or an intended feature.
+ WNDPROC oldwindow_proc =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(hwnd, GWLP_WNDPROC));
+ SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(proc));
+ return oldwindow_proc;
+}
+
+void* SetWindowUserData(HWND hwnd, void* user_data) {
+ return
+ reinterpret_cast<void*>(SetWindowLongPtr(hwnd, GWLP_USERDATA,
+ reinterpret_cast<LONG_PTR>(user_data)));
+}
+
+void* GetWindowUserData(HWND hwnd) {
+ return reinterpret_cast<void*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+}
+
+// Maps to the WNDPROC for a window that was active before the subclass was
+// installed.
+static const wchar_t* const kHandlerKey = L"__ORIGINAL_MESSAGE_HANDLER__";
+
+bool Subclass(HWND window, WNDPROC subclass_proc) {
+ WNDPROC original_handler =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
+ if (original_handler != subclass_proc) {
+ win_util::SetWindowProc(window, subclass_proc);
+ SetProp(window, kHandlerKey, original_handler);
+ return true;
+ }
+ return false;
+}
+
+bool Unsubclass(HWND window, WNDPROC subclass_proc) {
+ WNDPROC current_handler =
+ reinterpret_cast<WNDPROC>(GetWindowLongPtr(window, GWLP_WNDPROC));
+ if (current_handler == subclass_proc) {
+ HANDLE original_handler = GetProp(window, kHandlerKey);
+ if (original_handler) {
+ RemoveProp(window, kHandlerKey);
+ win_util::SetWindowProc(window,
+ reinterpret_cast<WNDPROC>(original_handler));
+ return true;
+ }
+ }
+ return false;
+}
+
+WNDPROC GetSuperclassWNDPROC(HWND window) {
+ return reinterpret_cast<WNDPROC>(GetProp(window, kHandlerKey));
+}
+
+#pragma warning(pop)
+
+bool IsShiftPressed() {
+ return (::GetKeyState(VK_SHIFT) & 0x80) == 0x80;
+}
+
+bool IsCtrlPressed() {
+ return (::GetKeyState(VK_CONTROL) & 0x80) == 0x80;
+}
+
+bool IsAltPressed() {
+ return (::GetKeyState(VK_MENU) & 0x80) == 0x80;
+}
+
+std::wstring GetClassName(HWND window) {
+ // GetClassNameW will return a truncated result (properly null terminated) if
+ // the given buffer is not large enough. So, it is not possible to determine
+ // that we got the entire class name if the result is exactly equal to the
+ // size of the buffer minus one.
+ DWORD buffer_size = MAX_PATH;
+ while (true) {
+ std::wstring output;
+ DWORD size_ret =
+ GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size);
+ if (size_ret == 0)
+ break;
+ if (size_ret < (buffer_size - 1)) {
+ output.resize(size_ret);
+ return output;
+ }
+ buffer_size *= 2;
+ }
+ return std::wstring(); // error
+}
+
+bool UserAccountControlIsEnabled() {
+ RegKey key(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System");
+ DWORD uac_enabled;
+ if (!key.ReadValueDW(L"EnableLUA", &uac_enabled))
+ return true;
+ return (uac_enabled == 1);
+}
+
+} // namespace win_util
+
+#ifdef _MSC_VER
+//
+// If the ASSERT below fails, please install Visual Studio 2005 Service Pack 1.
+//
+extern char VisualStudio2005ServicePack1Detection[10];
+COMPILE_ASSERT(sizeof(&VisualStudio2005ServicePack1Detection) == 4,
+ VS2005SP1Detect);
+//
+// Chrome requires at least Service Pack 1 for Visual Studio 2005.
+//
+#endif // _MSC_VER
+
+#ifndef COPY_FILE_COPY_SYMLINK
+#error You must install the Windows 2008 or Vista Software Development Kit and \
+set it as your default include path to build this library. You can grab it by \
+searching for "download windows sdk 2008" in your favorite web search engine.
+#endif
diff --git a/base/win_util.h b/base/win_util.h
new file mode 100644
index 0000000..9efb245
--- /dev/null
+++ b/base/win_util.h
@@ -0,0 +1,116 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_WIN_UTIL_H__
+#define BASE_WIN_UTIL_H__
+
+#include <windows.h>
+#include <aclapi.h>
+
+#include <string>
+
+namespace win_util {
+
+// NOTE: Keep these in order so callers can do things like
+// "if (GetWinVersion() > WINVERSION_2000) ...". It's OK to change the values,
+// though.
+enum WinVersion {
+ WINVERSION_PRE_2000 = 0, // Not supported
+ WINVERSION_2000 = 1,
+ WINVERSION_XP = 2,
+ WINVERSION_SERVER_2003 = 3,
+ WINVERSION_VISTA = 4,
+};
+
+void GetNonClientMetrics(NONCLIENTMETRICS* metrics);
+
+// Returns the running version of Windows.
+WinVersion GetWinVersion();
+
+// Adds an ACE in the DACL of the object referenced by handle. The ACE is
+// granting |access| to the user |known_sid|.
+// If |known_sid| is WinSelfSid, the sid of the current user will be added to
+// the DACL.
+bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid,
+ ACCESS_MASK access);
+
+// Returns the string representing the current user sid.
+bool GetUserSidString(std::wstring* user_sid);
+
+// Creates a security descriptor with a DACL that has one ace giving full
+// access to the current logon session.
+// The security descriptor returned must be freed using LocalFree.
+// The function returns true if it succeeds, false otherwise.
+bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor);
+
+// Useful for subclassing a HWND. Returns the previous window procedure.
+WNDPROC SetWindowProc(HWND hwnd, WNDPROC wndproc);
+
+// Subclasses a window, replacing its existing window procedure with the
+// specified one. Returns true if the current window procedure was replaced,
+// false if the window has already been subclassed with the specified
+// subclass procedure.
+bool Subclass(HWND window, WNDPROC subclass_proc);
+
+// Unsubclasses a window subclassed using Subclass. Returns true if
+// the window was subclassed with the specified |subclass_proc| and the window
+// was successfully unsubclassed, false if the window's window procedure is not
+// |subclass_proc|.
+bool Unsubclass(HWND window, WNDPROC subclass_proc);
+
+// Retrieves the original WNDPROC of a window subclassed using
+// SubclassWindow.
+WNDPROC GetSuperclassWNDPROC(HWND window);
+
+// Pointer-friendly wrappers around Get/SetWindowLong(..., GWLP_USERDATA, ...)
+// Returns the previously set value.
+void* SetWindowUserData(HWND hwnd, void* user_data);
+void* GetWindowUserData(HWND hwnd);
+
+// Returns true if the shift key is currently pressed.
+bool IsShiftPressed();
+
+// Returns true if the ctrl key is currently pressed.
+bool IsCtrlPressed();
+
+// Returns true if the alt key is currently pressed.
+bool IsAltPressed();
+
+// A version of the GetClassNameW API that returns the class name in an
+// std::wstring. An empty result indicates a failure to get the class name.
+std::wstring GetClassName(HWND window);
+
+// Returns false if the computer is running Vista and the user account control
+// is disabled. Returns true if user account control is enabled or the machine
+// is not running vista.
+bool UserAccountControlIsEnabled();
+
+} // namespace win_util
+
+#endif // BASE_WIN_UTIL_H__
diff --git a/base/win_util_unittest.cc b/base/win_util_unittest.cc
new file mode 100644
index 0000000..2374284
--- /dev/null
+++ b/base/win_util_unittest.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/win_util.h"
+
+// The test is somewhat silly, because the Vista bots some have UAC enabled
+// and some have it disabled. At least we check that it does not crash.
+TEST(BaseWinUtilTest, TestIsUACEnabled) {
+ if (win_util::GetWinVersion() == win_util::WINVERSION_VISTA) {
+ win_util::UserAccountControlIsEnabled();
+ } else {
+ EXPECT_TRUE(win_util::UserAccountControlIsEnabled());
+ }
+}
+
+TEST(BaseWinUtilTest, TestGetUserSidString) {
+ std::wstring user_sid;
+ EXPECT_TRUE(win_util::GetUserSidString(&user_sid));
+ EXPECT_TRUE(!user_sid.empty());
+}
+
+TEST(BaseWinUtilTest, TestGetNonClientMetrics) {
+ NONCLIENTMETRICS metrics = {0};
+ win_util::GetNonClientMetrics(&metrics);
+ EXPECT_TRUE(metrics.cbSize > 0);
+ EXPECT_TRUE(metrics.iScrollWidth > 0);
+ EXPECT_TRUE(metrics.iScrollHeight > 0);
+} \ No newline at end of file
diff --git a/base/windows_message_list.h b/base/windows_message_list.h
new file mode 100644
index 0000000..7a60fb0
--- /dev/null
+++ b/base/windows_message_list.h
@@ -0,0 +1,274 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// WARNING: DO NOT USE standard header file protection.
+// This file may be include several times in its entirety.
+
+// This file contains a list of all messages supported by Windows as would be
+// handled in a message loop. We only list the messages provided in
+// <winuser.h>, and do not currently include (the otherwise undefined)
+// #define WM_SYSTIMER 0x118
+
+// By using various macro tricks, this list can be used to create pretty print
+// functions for the messages. See message_loop.cc for an example.
+
+// Start list of Windows Messages given in <winuser.h>
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NULL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFOCUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KILLFOCUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENABLE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETREDRAW)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXTLENGTH)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLOSE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYENDSESSION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYOPEN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENDSESSION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUIT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ERASEBKGND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOLORCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SHOWWINDOW)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WININICHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTINGCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVMODECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATEAPP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_FONTCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELMODE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETCURSOR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHILDACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUEUESYNC)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETMINMAXINFO)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ICONERASEBKGND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTDLGCTL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SPOOLERSTATUS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MEASUREITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DELETEITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VKEYTOITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHARTOITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFONT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETFONT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETHOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETHOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYDRAGICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPAREITEM)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETOBJECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPACTING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMNOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPYDATA)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELJOURNAL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGEREQUEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TCARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HELP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USERCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFYFORMAT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CONTEXTMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DISPLAYCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETICON)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCDESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCALCSIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCHITTEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCPAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETDLGCODE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYNCPAINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT_DEVICE_CHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEADCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSDEADCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNICHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_STARTCOMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_ENDCOMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITION)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITDIALOG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENUPOPUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUSELECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERIDLE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENURBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUDRAG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUGETOBJECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNINITMENUPOPUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGEUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UPDATEUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYUISTATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORMSGBOX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLOREDIT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORLISTBOX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORBTN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORDLG)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSCROLLBAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSTATIC)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEWHEEL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDBLCLK)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHWHEEL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PARENTNOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERMENULOOP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITMENULOOP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CAPTURECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWERBROADCAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVICECHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICREATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIDESTROY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIACTIVATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIRESTORE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDINEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIMAXIMIZE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDITILE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICASCADE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIICONARRANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIGETACTIVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDISETMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERSIZEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITSIZEMOVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DROPFILES)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIREFRESHMENU)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SETCONTEXT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_NOTIFY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CONTROL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITIONFULL)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SELECT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CHAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_REQUEST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYDOWN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYUP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHOVER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELEAVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEHOVER)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSELEAVE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WTSSESSION_CHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_FIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_LAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CUT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PASTE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLEAR)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNDO)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERFORMAT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERALLFORMATS)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROYCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLLCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZECLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ASKCBFORMATNAME)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGECBCHAIN)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLLCLIPBOARD)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYNEWPALETTE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTEISCHANGING)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HOTKEY)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINTCLIENT)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APPCOMMAND)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_THEMECHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLIPBOARDUPDATE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOMPOSITIONCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMNCRENDERINGCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOLORIZATIONCOLORCHANGED)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMWINDOWMAXIMIZEDCHANGE)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTITLEBARINFOEX)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINFIRST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINLAST)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APP)
+A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USER)
+// End list of Windows Messages given in <winuser.h>
diff --git a/base/wmi_util.cc b/base/wmi_util.cc
new file mode 100644
index 0000000..e9159e8
--- /dev/null
+++ b/base/wmi_util.cc
@@ -0,0 +1,145 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+#include <atlbase.h>
+
+#pragma comment(lib, "wbemuuid.lib")
+
+#include "base/wmi_util.h"
+
+bool WMIUtil::CreateLocalConnection(bool set_blanket,
+ IWbemServices** wmi_services) {
+ CComPtr<IWbemLocator> wmi_locator;
+ HRESULT hr = wmi_locator.CoCreateInstance(CLSID_WbemLocator, NULL,
+ CLSCTX_INPROC_SERVER);
+ if (FAILED(hr))
+ return false;
+
+ CComPtr<IWbemServices> wmi_services_r;
+ hr = wmi_locator->ConnectServer(CComBSTR(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL,
+ 0, 0, &wmi_services_r);
+ if (FAILED(hr))
+ return false;
+
+ if (set_blanket) {
+ hr = ::CoSetProxyBlanket(wmi_services_r,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ NULL,
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL,
+ EOAC_NONE);
+ if (FAILED(hr))
+ return false;
+ }
+
+ *wmi_services = wmi_services_r.Detach();
+ return true;
+}
+
+bool WMIUtil::CreateClassMethodObject(IWbemServices* wmi_services,
+ const std::wstring& class_name,
+ const std::wstring& method_name,
+ IWbemClassObject** class_instance) {
+ // We attempt to instantiate a COM object that represents a WMI object plus
+ // a method rolled into one entity.
+ CComBSTR b_class_name(class_name.c_str());
+ CComBSTR b_method_name(method_name.c_str());
+ CComPtr<IWbemClassObject> class_object = NULL;
+ HRESULT hr;
+ hr = wmi_services->GetObject(b_class_name, 0, NULL, &class_object, NULL);
+ if (FAILED(hr))
+ return false;
+
+ CComPtr<IWbemClassObject> params_def = NULL;
+ hr = class_object->GetMethod(b_method_name, 0, &params_def, NULL);
+ if (FAILED(hr))
+ return false;
+
+ if (NULL == params_def) {
+ // You hit this special case if the WMI class is not a CIM class. MSDN
+ // sometimes tells you this. Welcome to WMI hell.
+ return false;
+ }
+
+ hr = params_def->SpawnInstance(0, class_instance);
+ return(SUCCEEDED(hr));
+}
+
+bool SetParameter(IWbemClassObject* class_method,
+ const std::wstring& parameter_name, VARIANT* parameter) {
+ HRESULT hr = class_method->Put(parameter_name.c_str(), 0, parameter, 0);
+ return SUCCEEDED(hr);
+}
+
+
+// The code in Launch() basically calls the Create Method of the Win32_Process
+// CIM class is documented here:
+// http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx
+
+bool WMIProcessUtil::Launch(const std::wstring& command_line, int* process_id) {
+ CComPtr<IWbemServices> wmi_local;
+ if (!WMIUtil::CreateLocalConnection(true, &wmi_local))
+ return false;
+
+ const wchar_t class_name[] = L"Win32_Process";
+ const wchar_t method_name[] = L"Create";
+ CComPtr<IWbemClassObject> process_create;
+ if (!WMIUtil::CreateClassMethodObject(wmi_local, class_name, method_name,
+ &process_create))
+ return false;
+
+ CComVariant b_command_line(command_line.c_str());
+ if (!SetParameter(process_create, L"CommandLine", &b_command_line))
+ return false;
+
+ CComPtr<IWbemClassObject> out_params;
+ HRESULT hr = wmi_local->ExecMethod(CComBSTR(class_name),
+ CComBSTR(method_name), 0, NULL,
+ process_create, &out_params, NULL);
+ if (FAILED(hr))
+ return false;
+
+ CComVariant ret_value;
+ hr = out_params->Get(L"ReturnValue", 0, &ret_value, NULL, 0);
+ if (FAILED(hr) || (0 != ret_value.uintVal))
+ return false;
+
+ CComVariant pid;
+ hr = out_params->Get(L"ProcessId", 0, &pid, NULL, 0);
+ if (FAILED(hr) || (0 == pid.intVal))
+ return false;
+
+ if (process_id)
+ *process_id = pid.intVal;
+
+ return true;
+}
diff --git a/base/wmi_util.h b/base/wmi_util.h
new file mode 100644
index 0000000..737b740
--- /dev/null
+++ b/base/wmi_util.h
@@ -0,0 +1,98 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// WMI (Windows Management and Instrumentation) is a big, complex, COM-based
+// API that can be used to perform all sorts of things. Sometimes is the best
+// way to accomplish something under windows but its lack of an approachable
+// C++ interface prevents its use. This collection of fucntions is a step in
+// that direction.
+// There are two classes; WMIUtil and WMIProcessUtil. The first
+// one contain generic helpers and the second one contains the only
+// functionality that is needed right now which is to use WMI to launch a
+// process.
+// To use any function on this header you must call CoInitialize or
+// CoInitializeEx beforehand.
+//
+// For more information about WMI programming:
+// http://msdn2.microsoft.com/en-us/library/aa384642(VS.85).aspx
+
+#ifndef BASE_WMI_UTIL_H__
+#define BASE_WMI_UTIL_H__
+
+#include <string>
+#include <wbemidl.h>
+
+class WMIUtil {
+ public:
+ // Creates an instance of the WMI service connected to the local computer and
+ // returns its COM interface. If 'set-blanket' is set to true, the basic COM
+ // security blanket is applied to the returned interface. This is almost
+ // always desirable unless you set the parameter to false and apply a custom
+ // COM security blanket.
+ // Returns true if succeeded and 'wmi_services': the pointer to the service.
+ // When done with the interface you must call Release();
+ static bool CreateLocalConnection(bool set_blanket,
+ IWbemServices** wmi_services);
+
+ // Creates a WMI method using from a WMI class named 'class_name' that
+ // contains a method named 'method_name'. Only WMI classes that are CIM
+ // classes can be created using this function.
+ // Returns true if succeeded and 'class_instance' returns a pointer to the
+ // WMI method that you can fill with parameter values using SetParameter.
+ // When done with the interface you must call Release();
+ static bool CreateClassMethodObject(IWbemServices* wmi_services,
+ const std::wstring& class_name,
+ const std::wstring& method_name,
+ IWbemClassObject** class_instance);
+
+ // Fills a single parameter given an instanced 'class_method'. Returns true
+ // if operation succeeded. When all the parameters are set the method can
+ // be executed using IWbemServices::ExecMethod().
+ static bool SetParameter(IWbemClassObject* class_method,
+ const std::wstring& parameter_name,
+ VARIANT* parameter);
+};
+
+// This class contains functionality of the WMI class 'Win32_Process'
+// more info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx
+class WMIProcessUtil {
+ public:
+ // Creates a new process from 'command_line'. The advantage over CreateProcess
+ // is that it allows you to always break out from a Job object that the caller
+ // is attached to even if the Job object flags prevent that.
+ // Returns true and the process id in process_id if the process is launched
+ // successful. False otherwise.
+ // Note that a fully qualified path must be specified in most cases unless
+ // the program is not in the search path of winmgmt.exe.
+ // Processes created this way are children of wmiprvse.exe and run with the
+ // caller credentials.
+ static bool Launch(const std::wstring& command_line, int* process_id);
+};
+
+#endif // BASE_WMI_UTIL_H__
diff --git a/base/wmi_util_unittest.cc b/base/wmi_util_unittest.cc
new file mode 100644
index 0000000..40853a4
--- /dev/null
+++ b/base/wmi_util_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <windows.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "base/wmi_util.h"
+
+TEST(WMIUtilTest, TestLocalConnectionSecurityBlanket) {
+ ::CoInitialize(NULL);
+ IWbemServices* services = NULL;
+ EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &services));
+ ASSERT_TRUE(NULL != services);
+ ULONG refs = services->Release();
+ EXPECT_EQ(refs, 0);
+ ::CoUninitialize();
+}
+
+TEST(WMIUtilTest, TestLocalConnectionNoSecurityBlanket) {
+ ::CoInitialize(NULL);
+ IWbemServices* services = NULL;
+ EXPECT_TRUE(WMIUtil::CreateLocalConnection(false, &services));
+ ASSERT_TRUE(NULL != services);
+ ULONG refs = services->Release();
+ EXPECT_EQ(refs, 0);
+ ::CoUninitialize();
+}
+
+TEST(WMIUtilTest, TestCreateClassMethod) {
+ ::CoInitialize(NULL);
+ IWbemServices* wmi_services = NULL;
+ EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &wmi_services));
+ ASSERT_TRUE(NULL != wmi_services);
+ IWbemClassObject* class_method = NULL;
+ EXPECT_TRUE(WMIUtil::CreateClassMethodObject(wmi_services,
+ L"Win32_ShortcutFile",
+ L"Rename", &class_method));
+ ASSERT_TRUE(NULL != class_method);
+ ULONG refs = class_method->Release();
+ EXPECT_EQ(refs, 0);
+ refs = wmi_services->Release();
+ EXPECT_EQ(refs, 0);
+ ::CoUninitialize();
+}
+
+// Creates an instance of cmd which executes 'echo' and exits immediately.
+TEST(WMIUtilTest, TestLaunchProcess) {
+ ::CoInitialize(NULL);
+ int pid = 0;
+ bool result = WMIProcessUtil::Launch(L"cmd.exe /c echo excelent!", &pid);
+ EXPECT_TRUE(result);
+ EXPECT_GT(pid, 0);
+ ::CoUninitialize();
+}
diff --git a/base/word_iterator.cc b/base/word_iterator.cc
new file mode 100644
index 0000000..1291630
--- /dev/null
+++ b/base/word_iterator.cc
@@ -0,0 +1,88 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/logging.h"
+#include "base/word_iterator.h"
+#include "unicode/ubrk.h"
+
+const int WordIterator::npos = -1;
+
+WordIterator::WordIterator(const std::wstring& str, BreakType break_type)
+ : iter_(NULL),
+ string_(str),
+ break_type_(break_type),
+ prev_(npos),
+ pos_(0) {
+}
+
+WordIterator::~WordIterator() {
+ if (iter_)
+ ubrk_close(iter_);
+}
+
+bool WordIterator::Init() {
+ UErrorCode status = U_ZERO_ERROR;
+ UBreakIteratorType break_type;
+ switch (break_type_) {
+ case BREAK_WORD:
+ break_type = UBRK_WORD;
+ break;
+ case BREAK_LINE:
+ break_type = UBRK_LINE;
+ break;
+ default:
+ NOTREACHED();
+ break_type = UBRK_LINE;
+ }
+ iter_ = ubrk_open(break_type, NULL,
+ string_.data(), static_cast<int32_t>(string_.size()),
+ &status);
+ if (U_FAILURE(status)) {
+ NOTREACHED() << "ubrk_open failed";
+ return false;
+ }
+ ubrk_first(iter_); // Move the iterator to the beginning of the string.
+ return true;
+}
+
+bool WordIterator::Advance() {
+ prev_ = pos_;
+ const int32_t pos = ubrk_next(iter_);
+ if (pos == UBRK_DONE) {
+ pos_ = npos;
+ return false;
+ } else {
+ pos_ = static_cast<int>(pos);
+ return true;
+ }
+}
+
+bool WordIterator::IsWord() const {
+ return (ubrk_getRuleStatus(iter_) != UBRK_WORD_NONE);
+} \ No newline at end of file
diff --git a/base/word_iterator.h b/base/word_iterator.h
new file mode 100644
index 0000000..fe86411
--- /dev/null
+++ b/base/word_iterator.h
@@ -0,0 +1,110 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_WORD_ITERATOR_H__
+#define BASE_WORD_ITERATOR_H__
+
+#include "base/basictypes.h"
+
+// The WordIterator class iterates through the words and word breaks
+// in a string. (In the string " foo bar! ", the word breaks are at the
+// periods in ". .foo. .bar.!. .".)
+//
+// To extract the words from a string, move a WordIterator through the
+// string and test whether IsWord() is true. E.g.,
+// WordIterator iter(str, WordIterator::BREAK_WORD);
+// if (!iter.Init()) return false;
+// while (iter.Advance()) {
+// if (iter.IsWord()) {
+// // region [iter.prev(),iter.pos()) contains a word.
+// LOG(INFO) << "word: " << iter.GetWord();
+// }
+// }
+
+
+class WordIterator {
+ public:
+ enum BreakType {
+ BREAK_WORD,
+ BREAK_LINE
+ };
+
+ // Requires |str| to live as long as the WordIterator does.
+ WordIterator(const std::wstring& str, BreakType break_type);
+ ~WordIterator();
+
+ // Init() must be called before any of the iterators are valid.
+ // Returns false if ICU failed to initialize.
+ bool Init();
+
+ // Return the current break position within the string,
+ // or WordIterator::npos when done.
+ int pos() const { return pos_; }
+ // Return the value of pos() returned before Advance() was last called.
+ int prev() const { return prev_; }
+
+ // A special position value indicating "end of string".
+ static const int npos;
+
+ // Advance to the next break. Returns false if we've run past the end of
+ // the string. (Note that the very last "word break" is after the final
+ // character in the string, and when we advance to that position it's the
+ // last time Advance() returns true.)
+ bool Advance();
+
+ // 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.)
+ bool IsWord() const;
+
+ // Return the word between prev() and pos().
+ // Advance() must have been called successfully at least once
+ // for pos() to have advanced to somewhere useful.
+ std::wstring GetWord() const {
+ DCHECK(prev_ >= 0 && pos_ >= 0);
+ return string_.substr(prev_, pos_ - prev_);
+ }
+
+ private:
+ // ICU iterator.
+ void* iter_;
+
+ // The string we're iterating over.
+ const std::wstring& string_;
+
+ // The breaking style (word/line).
+ BreakType break_type_;
+
+ // Previous and current iterator positions.
+ int prev_, pos_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(WordIterator);
+};
+
+#endif // BASE_WORD_ITERATOR_H__ \ No newline at end of file
diff --git a/base/worker_pool.cc b/base/worker_pool.cc
new file mode 100644
index 0000000..e70edad
--- /dev/null
+++ b/base/worker_pool.cc
@@ -0,0 +1,57 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "base/worker_pool.h"
+
+#include "base/logging.h"
+
+namespace {
+
+DWORD CALLBACK WorkItemCallback(void* param) {
+ Task* task = static_cast<Task*>(param);
+ task->Run();
+ WorkerPool::RecycleTask(task);
+ return 0;
+}
+
+} // namespace
+
+bool WorkerPool::Run(Task* task, bool slow) {
+ ULONG flags = 0;
+ if (slow)
+ flags |= WT_EXECUTELONGFUNCTION;
+
+ if (!QueueUserWorkItem(WorkItemCallback, task, flags)) {
+ DLOG(ERROR) << "QueueUserWorkItem failed: " << GetLastError();
+ RecycleTask(task);
+ return false;
+ }
+
+ return true;
+}
diff --git a/base/worker_pool.h b/base/worker_pool.h
new file mode 100644
index 0000000..3b0039f
--- /dev/null
+++ b/base/worker_pool.h
@@ -0,0 +1,50 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef BASE_WORKER_POOL_H_
+#define BASE_WORKER_POOL_H_
+
+#include "base/task.h"
+
+// This is a facility that runs tasks that don't require a specific thread or
+// a message loop.
+class WorkerPool {
+ public:
+ // This function posts |task| to run on a worker thread. |slow| should be used
+ // for tasks that will take a long time to execute. |task| will be recycled
+ // even if the function fails.
+ static bool Run(Task* task, bool slow);
+
+ // Recycles the task.
+ static void RecycleTask(Task* task) {
+ task->RecycleOrDelete();
+ }
+};
+
+#endif // BASE_WORKER_POOL_H_